<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0"><channel><title>yingye's Blog</title><link>https://blog.yingye.site</link><atom:link href="https://blog.yingye.site/rss.xml" rel="self" type="application/rss+xml"/><description>yingye's Blog</description><generator>Halo v2.22.14</generator><language>zh-cn</language><image><url>https://blog.yingye.site/upload/2021/05/favicon-0a1787dbb3e8498f9c41c97e97389bf0.png</url><title>yingye's Blog</title><link>https://blog.yingye.site</link></image><lastBuildDate>Mon, 11 May 2026 11:21:21 GMT</lastBuildDate><item><title><![CDATA[[学习,记录,整理] Windows使用WinSW注册Java程序为系统服务]]></title><link>https://blog.yingye.site/2026/02/20/windows%E4%BD%BF%E7%94%A8winsw%E6%B3%A8%E5%86%8Cjava%E7%A8%8B%E5%BA%8F%E4%B8%BA%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20Windows%E4%BD%BF%E7%94%A8WinSW%E6%B3%A8%E5%86%8CJava%E7%A8%8B%E5%BA%8F%E4%B8%BA%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1&amp;url=/2026/02/20/windows%E4%BD%BF%E7%94%A8winsw%E6%B3%A8%E5%86%8Cjava%E7%A8%8B%E5%BA%8F%E4%B8%BA%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1" width="1" height="1" alt="" style="opacity:0;">
<h2 id="系统环境">系统环境</h2>
<ul>
 <li>OS: Windows11 26H1(28000.1575)</li>
 <li>IP: 192.168.0.108</li>
 <li>Java: OracleJDK-25.0.1</li>
 <li>Jar文件: PaperMC-1.21.11-69</li>
 <li>WinSW: WinSW v3.0.0-alpha.11</li>
 <li>MCRCON: v0.7.2-windows-x86-64</li>
</ul>
<p>本次使用的文件路径</p>
<ul>
 <li>jre: C:/Software/minijre-25</li>
 <li>paper: C:/Software/paper</li>
</ul>
<br>
<h2 id="部署">部署</h2>
<h3 id="下载WinSW并添加配置文件">下载WinSW并添加配置文件</h3>
<p>下载地址<a href="https://github.com/winsw/winsw/releases">https://github.com/winsw/winsw/releases</a> ，这里下载的为<a href="https://github.com/winsw/winsw/releases/download/v3.0.0-alpha.11/WinSW-net461.exe">WinSW-net461.exe</a>文件体积小，下载后放在<code>C:/Software/paper/service</code>文件夹下这里重命名为<code>service.exe</code>，因为WinSW运行会创建几个日志文件，直接放在paper根目录显乱。
 <br>
 <br>
 <br>
 再在<code>C:/Software/paper/service</code>文件夹下创建<code>service.xml</code>文件</p>
<pre><code class="language-xml">&lt;service&gt;
  &lt;!-- 服务注册名 --&gt;
  &lt;id&gt;papermc&lt;/id&gt;
  &lt;!-- 这里的内容在任务管理器中出现在描述栏，在服务中显示在名称栏 --&gt;
  &lt;name&gt;PaperMC Server&lt;/name&gt;
  &lt;!-- 这里的内容在服务中显示在描述栏 --&gt;
  &lt;description&gt;PaperMC服务器程序&lt;/description&gt;
  &lt;!-- 程序的工作文件夹，根据实际修改 --&gt;
  &lt;workingdirectory&gt;C:/Software/paper&lt;/workingdirectory&gt;
  &lt;!-- Java的可执行文件路径，根据实际修改 --&gt;
  &lt;executable&gt;C:/Software/minijre-25/bin/java.exe&lt;/executable&gt;
  &lt;!-- 启动参数，Jar文件的路径根据实际修改 --&gt;
  &lt;arguments&gt;-server -Xms4G -Xmx4G -jar C:/Software/paper/paper.jar nogui&lt;/arguments&gt;
  &lt;log mode="roll" /&gt;

  &lt;!-- 启动服务用的账户 --&gt;
  &lt;!-- 这里不知道为什么不生效，后面安装好服务了在手动改一下 --&gt;
  &lt;serviceaccount&gt;
    &lt;domain&gt;NT AUTHORITY&lt;/domain&gt;
    &lt;user&gt;NetworkService&lt;/user&gt;
    &lt;password&gt;&lt;/password&gt;
    &lt;allowservicelogon&gt;true&lt;/allowservicelogon&gt;
  &lt;/serviceaccount&gt;
&lt;/service&gt;
</code></pre>
<p>目前C:/Software/paper/service下的文件
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage.png&amp;size=m" alt="C:/Software/paper/service"></p>
<h3 id="安装为服务">安装为服务</h3>
<p>右键底部的Windows徽标(就4个方块组成的那个)，以管理员权限打开终端/cmd/powershell
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771587438836.png&amp;size=m" alt="如何使用管理员权限打开终端"></p>
<p>输入以下命令，文件路径根据自己的实际情况进行修改，前面为WinSW文件路径，后面为配置文件的路径</p>
<pre><code class="language-bash">C:\Software\paper\service\service.exe install C:\Software\paper\service\service.xml
</code></pre>
<p>出现如下结果则安装成功
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771587758313.png&amp;size=m" alt="成功安装为服务"></p>
<p>接下来还需要修改一下启动服务用的账户，不知道为什么注册服务时指定的<code>user</code>不生效，输入以下命令即可修改</p>
<pre><code class="language-bash">cmd /k sc config papermc obj= "NT AUTHORITY\NetworkService"
</code></pre>
<p>这里的<code>papermc</code>根据配置文件里id这一项的内容修改<code>&lt;id&gt;papermc&lt;/id&gt;</code>，例如配置文件为<code>&lt;id&gt;MyPaper&lt;/id&gt;</code>，则对应执行的为</p>
<pre><code class="language-bash">cmd /k sc config MyPaper obj= "NT AUTHORITY\NetworkService"
</code></pre>
<p>执行结果如下
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771588343302.png&amp;size=m" alt="成功修改服务使用的用户"></p>
<h3 id="启动服务">启动服务</h3>
<p>输入以下命令即可启动服务，这里的<code>papermc</code>根据实际注册的服务名进行修改。</p>
<pre><code class="language-bash">net start papermc
</code></pre>
<p>需要注意的是，如果是正常在命令行中启动服务，windows会询问是否允许Java使用端口进行通信，但以服务形式运行是不会弹出提示的，这样外部就无法连接到服务器，所以在以服务形式运行前，需要先在命令行中运行一次以放行端口。</p>
<br>
<h2 id="启用Paper的远程控制">启用Paper的远程控制</h2>
<p>按照以上步骤，虽然能在无窗口的情况下，做到开机自动启动服务器，但因为没有窗口了，也就无法管理服务器，这时候就需要使用到RCON了。</p>
<br>
<h3 id="修改配置文件">修改配置文件</h3>
<p>先关闭服务器，可以执行<code>net stop papermc</code>来停止服务，也可以在任务管理器中找到对应的服务按下停止；
 <br>
 再打开Paper文件夹下的<code>server.properties</code>文件(用记事本打开就行)，这里的示例文件夹为<code>C:/Software/paper</code>，将<code>enable-rcon</code>这一行等号后面的内容改为<code>true</code>。
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771589348447.png&amp;size=m" alt="C:/Software/paper/server.properties"></p>
<p>在<code>rcon.password</code>这一行的等号后面填写上验证用的密码
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771589440111.png&amp;size=m" alt="C:/Software/paper/server.properties">
 <br>
 <code>rcon.port</code>为RCON服务使用的端口，这里可以保持默认</p>
<br>
<h3 id="使用MCRCON连接到服务端">使用MCRCON连接到服务端</h3>
<p>下载文件<a href="https://github.com/Tiiffi/mcrcon/releases">https://github.com/Tiiffi/mcrcon/releases</a> ，这里下载的为<code>mcrcon-0.7.2-windows-x86-64</code>的这一个，将压缩包内的mcrcon文件解压出来找个位置放，该程序不提供图形化界面，仅能通过命令行使用，使用示例如下</p>
<pre><code class="language-bash">I:\_tmp\mcrcon.exe -H 192.168.0.108 -P 25575 -p test#pwd
</code></pre>
<p>mcrcon的文件路径根据自己实际存放位置修改
 <br>
 -H 后面指定服务器IP
 <br>
 -P 后面指定服务器的RCON端口
 <br>
 -p 后面写上连接用的密码
 <br>
 这里需注意端口和密码的两个<code>p</code>的大小写
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2026%2F02%2Fimage-1771590460225.png&amp;size=m" alt="MCRCON成功连接"></p>]]></description><guid isPermaLink="false">/2026/02/20/windows%E4%BD%BF%E7%94%A8winsw%E6%B3%A8%E5%86%8Cjava%E7%A8%8B%E5%BA%8F%E4%B8%BA%E7%B3%BB%E7%BB%9F%E6%9C%8D%E5%8A%A1</guid><dc:creator>Administrator</dc:creator><category>我的世界</category><category>Windows</category><category>默认分类</category><category>Java</category><pubDate>Fri, 20 Feb 2026 11:28:14 GMT</pubDate></item><item><title><![CDATA[[记录,生活] 猫猫糕可爱涅]]></title><link>https://blog.yingye.site/2024/08/22/%E7%8C%AB%E7%8C%AB%E7%B3%95%E5%8F%AF%E7%88%B1%E6%B6%85</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%2C%E7%94%9F%E6%B4%BB%5D%20%E7%8C%AB%E7%8C%AB%E7%B3%95%E5%8F%AF%E7%88%B1%E6%B6%85&amp;url=/2024/08/22/%E7%8C%AB%E7%8C%AB%E7%B3%95%E5%8F%AF%E7%88%B1%E6%B6%85" width="1" height="1" alt="" style="opacity:0;">
<p>跟风朋友一起买了异宠拾遗的盲袋，等了一个多月可算发货了</p>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2024%2F08%2F6409d34d523655ca7436524a_1724335374126_4EOvlSaD3bGZ5Nc6Qa3Ua36QDzeohDMU.jpeg&amp;size=m" alt="6409d34d523655ca7436524a_1724335374126_4EOvlSaD3bGZ5Nc6Qa3Ua36QDzeohDMU"></p>
<br>
<p>纯属图一乐，没想到居然能出两个隐藏款的开拓猫猫糕</p>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2024%2F08%2F6409d34d523655ca7436524a_1724333863595_NsFtwbwRUnk0s5OMB2wVRz0jHkkRzBEP.jpeg&amp;size=m" alt="6409d34d523655ca7436524a_1724333863595_NsFtwbwRUnk0s5OMB2wVRz0jHkkRzBEP"></p>
<p>从未有过如此美妙的开局[]~(￣▽￣)~*</p>]]></description><guid isPermaLink="false">/2024/08/22/%E7%8C%AB%E7%8C%AB%E7%B3%95%E5%8F%AF%E7%88%B1%E6%B6%85</guid><dc:creator>Administrator</dc:creator><category>崩坏: 星穹铁道</category><category>游戏</category><category>默认分类</category><pubDate>Thu, 22 Aug 2024 14:06:43 GMT</pubDate></item><item><title><![CDATA[[记录,游戏] 谢谢你多年来的陪伴-崩坏三]]></title><link>https://blog.yingye.site/2023/03/06/%E8%B0%A2%E8%B0%A2%E4%BD%A0%E5%A4%9A%E5%B9%B4%E6%9D%A5%E7%9A%84%E9%99%AA%E4%BC%B4-%E5%B4%A9%E5%9D%8F%E4%B8%89</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%2C%E6%B8%B8%E6%88%8F%5D%20%E8%B0%A2%E8%B0%A2%E4%BD%A0%E5%A4%9A%E5%B9%B4%E6%9D%A5%E7%9A%84%E9%99%AA%E4%BC%B4-%E5%B4%A9%E5%9D%8F%E4%B8%89&amp;url=/2023/03/06/%E8%B0%A2%E8%B0%A2%E4%BD%A0%E5%A4%9A%E5%B9%B4%E6%9D%A5%E7%9A%84%E9%99%AA%E4%BC%B4-%E5%B4%A9%E5%9D%8F%E4%B8%89" width="1" height="1" alt="" style="opacity:0;">
<p>正好今天累积登舰1500天，昨天也把终章打完了，明明想一股脑的吐槽终章最后几节的处理和这些年来的谜之操作，但却又不知道该怎么讲述出来。
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%2528412%2529.png&amp;size=m" alt="1500天登舰)"></p>
<br>
<p>打完主线后发现绘卷的二和三开了，但已经没有了我的位置，没想到会在这种时候留下遗憾。
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%2528411%2529.png&amp;size=m" alt="绘卷一">
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%2528409%2529.png&amp;size=m" alt="绘卷二">
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%2528410%2529.png&amp;size=m" alt="绘卷三"></p>
<br>
<p>我对<code>你</code>想说的话，也许正如我设定的<code>核心密钥</code>一般吧。
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%2528413%2529.png&amp;size=m" alt="谢谢">
 <br>
 谢谢。</p>]]></description><guid isPermaLink="false">/2023/03/06/%E8%B0%A2%E8%B0%A2%E4%BD%A0%E5%A4%9A%E5%B9%B4%E6%9D%A5%E7%9A%84%E9%99%AA%E4%BC%B4-%E5%B4%A9%E5%9D%8F%E4%B8%89</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2023%2F03%2F2019111410133010929-1678110386974.jpg&amp;size=m" type="image/jpeg" length="0"/><category>崩坏3</category><category>游戏</category><category>默认分类</category><pubDate>Mon, 6 Mar 2023 14:11:19 GMT</pubDate></item><item><title><![CDATA[[学习,记录,整理] IndexedDB和FileSystemFileHandle]]></title><link>https://blog.yingye.site/2022/11/26/indexeddb%E5%92%8Cfilesystemfilehandle</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20IndexedDB%E5%92%8CFileSystemFileHandle&amp;url=/2022/11/26/indexeddb%E5%92%8Cfilesystemfilehandle" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E4%BA%8B%E5%8F%91%E6%80%BB%E6%9C%89%E4%B8%AA%E5%8E%9F%E5%9B%A0%E2%80%A6" tabindex="-1">事发总有个原因…</h2>
<p>同学的业务有个需求，文件需要断点续传，用到了IndexedDB来做状态保存，但前人写的代码复制过来在保存<code>file</code>对象上有点问题，就去帮忙了，顺便了解新知识。</p>
<br>
<h2 id="indexeddb" tabindex="-1">IndexedDB</h2>
<p><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API" target="_blank">IndexedDB</a>是运行在网页的数据库，可以直接将对象保存在其中。</p>
<h3 id="%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E6%8D%AE%E5%BA%93" tabindex="-1">初始化数据库</h3>
<pre><code class="language-javascript">// 打开IndexedDB数据库连接
async function openDatabase(dbName, version) {
  // 先获取当前站点下创建的所有数据库
  const databases = await window.indexedDB.databases();
  let flag = false;
  // 判断是否已经创建了想要的数据库，没有则更改标识，之后要做处理
  if (databases.filter(database =&gt; database.name == dbName).length == 0) {
    flag = true;
  }

  return new Promise((resolve, reject) =&gt; {
    // 数据库打开请求
    /** @type {IDBOpenDBRequest} */
    const DBOpenRequest = window.indexedDB.open(dbName, version);

    // 若数据库未创建，则需要额外做处理
    if (flag) {
      // 数据库版本更新时会触发该回调，初次创建时也会触发
      DBOpenRequest.onupgradeneeded = (event) =&gt; {
        // 数据库对象
        /** @type {IDBDatabase} */
        const d = event.target.result

        // 创建表，表只能在数据库版本更新的回调中进行创建。
        initDatabaseStruct(d)

        // 测试下来，当数据库不存在时，创建的连接是不能用于存储数据的，需要关闭后重新获取连接
        d.close();

        // 再次执行当前函数，并resolve二次执行的结果
        resolve(openDatabase(dbName, version));
      }
      DBOpenRequest.onerror = (event) =&gt; {
        reject(event.error)
      }
    } else {
      DBOpenRequest.onsuccess = (event) =&gt; {
        resolve(event.target.result)
      }
      DBOpenRequest.onerror = (event) =&gt; {
        reject(event.error)
      }
    }
  });
}

// 在数据库版本升级时对数据库做初始化操作
function initDatabaseStruct(/** @type {IDBDatabase} */ db) {
  db.createObjectStore('fileStore', {
    autoIncrement: true
  });
}
</code></pre>
<h3 id="%E6%B7%BB%E5%8A%A0%E6%95%B0%E6%8D%AE" tabindex="-1">添加数据</h3>
<pre><code class="language-javascript">// 保存数据到指定的表中
function saveData(/** @type {IDBDatabase} */ db, /** @type {String} */ storeName, /** @type {String} */ key, /** @type {Object} */ data) {
  return new Promise((resolve, reject) =&gt; {
    if (!db.objectStoreNames.contains(storeName)) {
      reject(`store '${storeName}' is missing`)
      return;
    }
    const store = db.transaction(storeName, 'readwrite').objectStore(storeName);
    /// 比较奇怪的顺序，平时都是先key再data，但这里应该是因为key是可选项，所以data在前
    store.put(data, key);
    resolve();
  });
}
</code></pre>
<h3 id="%E8%AF%BB%E5%8F%96%E6%95%B0%E6%8D%AE" tabindex="-1">读取数据</h3>
<pre><code class="language-javascript">// 从指定的表中读取数据
function getData(/** @type {IDBDatabase} */ db, /** @type {String} */ storeName, /** @type {String} */ key) {
  return new Promise((resolve, reject) =&gt; {
    if (!db.objectStoreNames.contains(storeName)) {
      reject(`store '${storeName}' is missing`)
      return;
    }
    // 本以为读取数据可以只给个read，但是报错了
    const store = db.transaction(storeName, 'readwrite').objectStore(storeName);
    const request = store.get(key);
    request.onerror = (event) =&gt; {
      reject(event.error)
    }
    request.onsuccess = (event) =&gt; {
      resolve(event.target.result)
    }
  });
}
</code></pre>
<br>
<h2 id="filesystemfilehandle" tabindex="-1"><a href="https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle" target="_blank">FileSystemFileHandle</a></h2>
<p>才知道原来除了<code>file</code>类型的<code>input</code>标签外，在window上也有能选取文件的方法: <code>showOpenFilePicker</code>，该方法选取完之后会返回一个<code>FileSystemFileHandle</code>类型的数组(因为可以设置为多选，所以才是数组吧)。</p>
<p>对该handle对象的原型上有<code>getFile</code>方法，可以用来获取对应的文件，该方法返回是一个Promise对象(不是添加回调好文明)。</p>
<br>
<p>有趣的来了，IndexedDB可以直接保存对象，哪怕是File对象也行，包括handle对象，所以可以这么做把选中的文件存放到IndexedDB里面，文件没上传完下次打开网站也可以继续读取。</p>
<pre><code class="language-javascript">  // 选取文件
  async function pickTheFile() {
    const pickerOpts = {
      types: [
        {
          description: 'Images',
          accept: {
            'image/*': ['.png', '.gif', '.jpeg', '.jpg'],
          },
        },
      ],
      excludeAcceptAllOption: true,
      multiple: true,
    };

    /** @type {Array&lt;FileSystemFileHandle&gt;} */
    const [filehandle] = await window.showOpenFilePicker(pickerOpts);
    const fileData = await filehandle.getFile();
    
    const db = await openDatabase('test', 1);
    await saveData(db, 'fileStore', 'img', fileData);
  }

  // 打开IndexedDB数据库连接
  async function openDatabase(dbName, version) {
    const databases = await window.indexedDB.databases();
    let flag = false;
    if (databases.filter(database =&gt; database.name == dbName).length == 0) {
      flag = true;
    }

    return new Promise((resolve, reject) =&gt; {
      /** @type {IDBOpenDBRequest} */
      const DBOpenRequest = window.indexedDB.open(dbName, version);

      if (flag) {
        DBOpenRequest.onupgradeneeded = (event) =&gt; {
          const d = event.target.result
          initDatabaseStruct(d)
          // 测试下来，当数据库不存在时，创建的连接是不能用于存储数据的，需要关闭后重新获取连接
          d.close();
          // 用callee重新执行当前函数，并resolve二次执行的结果
          resolve(openDatabase(dbName, version));
        }
        DBOpenRequest.onerror = (event) =&gt; {
          reject(event.error)
        }
      } else {
        DBOpenRequest.onsuccess = (event) =&gt; {
          resolve(event.target.result)
        }
        DBOpenRequest.onerror = (event) =&gt; {
          reject(event.error)
        }
      }
    });
  }

  // 在数据库版本升级后对数据库做初始化操作
  function initDatabaseStruct(/** @type {IDBDatabase} */ db) {
    db.createObjectStore('fileStore', {
      autoIncrement: true
    });
  }

  // 保存数据到指定的表中
  function saveData(/** @type {IDBDatabase} */ db, /** @type {String} */ storeName, /** @type {String} */ key, /** @type {Object} */ data) {
    return new Promise((resolve, reject) =&gt; {
      if (!db.objectStoreNames.contains(storeName)) {
        reject(`store '${storeName}' is missing`)
        return;
      }
      const store = db.transaction(storeName, 'readwrite').objectStore(storeName);
      /// 比较奇怪的顺序，平时都是先key再data，但这里应该是因为key是可选项，所以data在前
      store.put(data, key);
      resolve();
    });
  }
</code></pre>
<br>
<p>不过存File文件太大了，也可以选择将handle对象存入IndexedDB；但这种做法也存在一些问题。</p>
<ol>
 <li>handle的访问授权仅能在本次会话里存在，如果将网页关闭后(<strong>Microsoft Edge 107版本在地址栏有提示，可以手动删除授权</strong>)，再将handle对象读取出来后，需要调用handle对象上的<code>requestPermission</code>方法再次申请访问授权，该方法会返回一个Promise对象，用户点击允许后则能继续使用<code>getFile</code>获取文件；如果不申请授权直接调用<code>getFile</code>则会报错，这个应该是出于安全考虑。</li>
 <li>handle像是记录的文件路径，如果将磁盘上的文件删除，那么<code>getFile</code>就获取不到文件了。</li>
</ol>
<p>感觉就像在用iptables来添加规则，重启/手动删除前一直生效，重启/删除后就要重新添加规则了。</p>
<br>
<p>图片存储和读取展示示例：<a href="https://blog.yingye.site/htmlUtil/IndexedDB-Test.html" target="_blank">IndexedDB测试</a>，上面的代码片段都只复制了部分，可能有哪里忘了更改，建议Ctrl + S保存到本地文件，方便查看和预览。</p>]]></description><guid isPermaLink="false">/2022/11/26/indexeddb%E5%92%8Cfilesystemfilehandle</guid><dc:creator>Administrator</dc:creator><category>JavaScript</category><category>HTML</category><category>编程语言</category><category>默认分类</category><pubDate>Sat, 26 Nov 2022 11:53:17 GMT</pubDate></item><item><title><![CDATA[[记录] 注册表修改jar文件打开方式]]></title><link>https://blog.yingye.site/2022/10/27/%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BF%AE%E6%94%B9jar%E6%96%87%E4%BB%B6%E6%89%93%E5%BC%80%E6%96%B9%E5%BC%8F</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BF%AE%E6%94%B9jar%E6%96%87%E4%BB%B6%E6%89%93%E5%BC%80%E6%96%B9%E5%BC%8F&amp;url=/2022/10/27/%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BF%AE%E6%94%B9jar%E6%96%87%E4%BB%B6%E6%89%93%E5%BC%80%E6%96%B9%E5%BC%8F" width="1" height="1" alt="" style="opacity:0;">
<h2 id="简易版-2022-12-1更新">简易版 2022-12-1更新</h2>
<p>找到自己电脑上的javaw文件，按住shift再右键，可以发现菜单选项里多了一个复制为路径的选项，点击复制路径，然后打开记事本，将复制的路径粘贴进去，复制的路径是单个<code>\</code>的，需要手动在每一个<code>\</code>后面再加上一个<code>\</code>，然后只复制双引号中的内容，双引号就别复制了。</p>
<p>处理好后就可以下载<a href="https://blog.yingye.site/jar.reg">这个文件</a>，右键编辑，将其中</p>
<pre><code class="prism language-shell">C:\\Program Files\\Java\\jre\\bin\\javaw.exe
</code></pre>
<p>的部分修改为刚刚处理好的javaw的路径 ，修改后保存并双击该文件。</p>
<br>
<h2 id="操作">操作</h2>
<p>按下Win + R，在输入框中输入<code>regedit</code>，打开注册表编辑器。</p>
<p>复制路径填写到注册表编辑器顶部的路径中可以直接跳转到对应的项。</p>
<br>
<h3 id="hkey_classes_root.jar">\HKEY_CLASSES_ROOT\.jar</h3>
<p>选中.jar项，打开右边的默认项，按照下面的格式进行修改。</p>
<pre><code class="prism language-shell">"C:\Program Files\Java\jre\bin\javaw.exe" -jar "%1"
</code></pre>
<p>javaw.exe的路径根据自己电脑上java的安装路径进行更改。</p>
<br>
<h3 id="hkey_classes_rootjarfileshellopencommand">\HKEY_CLASSES_ROOT\jarfile\shell\open\command</h3>
<p>选中command项，打开右边的默认项，按照下面的格式进行修改</p>
<pre><code class="prism language-shell">"C:\Program Files\Java\jre\bin\javaw.exe" -jar "%1" %*
</code></pre>
<p>javaw.exe的路径根据自己电脑上java的安装路径进行更改。</p>
<p>如果以上项不存在，则根据上面的路径手动创建对应层级的项。</p>
<br>
<h2 id="参考">参考</h2>
<p><a href="https://www.jianshu.com/p/44ab80e80b83">[Java] jar文件运行（修改注册表修）</a></p>
<p><a href="https://www.cnblogs.com/fengliu-/p/10688257.html">通过注册表修改默认打开方式</a></p>]]></description><guid isPermaLink="false">/2022/10/27/%E6%B3%A8%E5%86%8C%E8%A1%A8%E4%BF%AE%E6%94%B9jar%E6%96%87%E4%BB%B6%E6%89%93%E5%BC%80%E6%96%B9%E5%BC%8F</guid><dc:creator>Administrator</dc:creator><category>Windows</category><category>默认分类</category><pubDate>Thu, 27 Oct 2022 12:55:55 GMT</pubDate></item><item><title><![CDATA[[学习,记录,整理] Nuxt处理i18n及路由]]></title><link>https://blog.yingye.site/2022/10/24/nuxt%E5%A4%84%E7%90%86i18n%E5%8F%8A%E8%B7%AF%E7%94%B1</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20Nuxt%E5%A4%84%E7%90%86i18n%E5%8F%8A%E8%B7%AF%E7%94%B1&amp;url=/2022/10/24/nuxt%E5%A4%84%E7%90%86i18n%E5%8F%8A%E8%B7%AF%E7%94%B1" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E5%88%9B%E5%BB%BAnuxt%E9%A1%B9%E7%9B%AE" tabindex="-1">创建Nuxt项目</h2>
<pre><code class="language-shell">npm init nuxt-app &lt;project-name&gt;
</code></pre>
<br>
<h2 id="i18n" tabindex="-1">i18n</h2>
<h3 id="%E5%BC%95%E5%85%A5i18n%E5%BA%93" tabindex="-1">引入i18n库</h3>
<pre><code class="language-shell">npm install @nuxtjs/i18n
</code></pre>
<br>
<h3 id="%E6%B7%BB%E5%8A%A0i18n%E9%85%8D%E7%BD%AE" tabindex="-1">添加i18n配置</h3>
<p>nuxt.config.js</p>
<pre><code class="language-javascript">export default {
    ...
    modules: [
        ...
        '@nuxtjs/i18n'
    ],
    i18n: {
        // 提供支持的语言
        locales: ['en', 'zh', 'tw'],
        // 直接从 / 访问时使用的默认语言
        defaultLocale: 'en',
        vueI18n: {
            // 当访问某个语言时找不到对应的配置，使用一个指定的语言进行替代
            fallbackLocale: 'en',
            messages: {
                en: {
                    welcome: 'Welcome to your Nuxt Application',
                    fb: 'fallback'
                },
                zh: {
                    welcome: '欢迎来到你的Nuxt应用程序',
                    fb: '回落'
                },
                tw: {
                    welcome: '歡迎來到你的Nuxt應用程式'
                }
            }
        },
        // 使用cookie缓存切换的语言
        detectBrowserLanguage: {
            useCookie: true,
            cookieKey: 'custom_cookie'
        }
    }
}
</code></pre>
<br>
<h3 id="%E5%9C%A8%E9%A1%B5%E9%9D%A2%E4%B8%AD%E4%BD%BF%E7%94%A8" tabindex="-1">在页面中使用</h3>
<p>components/Tutorial.vue</p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div class="relative flex items-top justify-center min-h-screen bg-gray-100 sm:items-center sm:pt-0"&gt;
        &lt;link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.1.2/dist/tailwind.min.css" rel="stylesheet"&gt;
        &lt;div class="max-w-4xl mx-auto sm:px-6 lg:px-8"&gt;
            ...
            &lt;div class="mt-8 bg-white overflow-hidden shadow sm:rounded-lg p-6"&gt;
                &lt;h2 class="text-2xl leading-7 font-semibold"&gt;
                    {{ $t("welcome") }}
                &lt;/h2&gt;
                ...
                &lt;div class="text-center mt-4"&gt;
                    &lt;nuxt-link :to="localePath('index', 'en')"&gt;English&lt;/nuxt-link&gt;
                    &lt;nuxt-link :to="localePath('index', 'zh')"&gt;简体中文&lt;/nuxt-link&gt;
                    &lt;nuxt-link :to="localePath('index', 'tw')"&gt;繁體中文&lt;/nuxt-link&gt;
                    &lt;p&gt;{{ $t("fb") }}&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            ...
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;
</code></pre>
<p><code>$t</code>、<code>localePath</code>、<code>switchLocalePath</code>等方法由<code>@nuxtjs/i18n</code>挂在到Vue对象/原型上，可以直接使用。</p>
<br>
<h2 id="%E5%8F%82%E8%80%83" tabindex="-1">参考</h2>
<p><a href="https://i18n.nuxtjs.org/setup" target="_blank">nuxt-i18n-module</a></p>]]></description><guid isPermaLink="false">/2022/10/24/nuxt%E5%A4%84%E7%90%86i18n%E5%8F%8A%E8%B7%AF%E7%94%B1</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2022%2F10%2Fnuxt-logo.ico&amp;size=m" type="image/jpeg" length="0"/><category>vue</category><category>JavaScript</category><category>编程语言</category><category>默认分类</category><pubDate>Mon, 24 Oct 2022 05:48:40 GMT</pubDate></item><item><title><![CDATA[[记录] 禁用并删除格式工厂自带的BrightData]]></title><link>https://blog.yingye.site/2022/08/17/%E8%AE%B0%E5%BD%95%E7%A6%81%E7%94%A8%E5%B9%B6%E5%88%A0%E9%99%A4%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82%E8%87%AA%E5%B8%A6%E7%9A%84brightdata</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20%E7%A6%81%E7%94%A8%E5%B9%B6%E5%88%A0%E9%99%A4%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82%E8%87%AA%E5%B8%A6%E7%9A%84BrightData&amp;url=/2022/08/17/%E8%AE%B0%E5%BD%95%E7%A6%81%E7%94%A8%E5%B9%B6%E5%88%A0%E9%99%A4%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82%E8%87%AA%E5%B8%A6%E7%9A%84brightdata" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E8%B5%B7%E5%9B%A0" tabindex="-1">起因</h2>
<p>老家电脑上的格式工厂是4.9.x版本的，无法正常调用AMD显卡加速，遂选择升级到新版。</p>
<p>今天在退出qbittorrent的时候，发现收起的托盘里多出了一个不认识的东西，名字指向格式工厂，文件在格式工厂安装目录下的<code>luminati</code>文件夹中。</p>
<p>搜索了一下这东西，貌似是把你的电脑纳入代理ip池中，使用<code>luminati</code>的人可以通过你的网络去发起请求，就像是梯子那样。</p>
<br>
<h2 id="%E5%88%A0%E9%99%A4" tabindex="-1">删除</h2>
<h3 id="%E9%80%80%E5%87%BA%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82%E5%92%8Cluminati" tabindex="-1">退出格式工厂和luminati</h3>
<p>这一步是为了将<code>luminati</code>文件夹下的文件删光，格式工厂在启动时会对<code>luminati</code>文件夹下的dll有引用。</p>
<h3 id="%E6%B8%85%E9%99%A4%E6%9D%83%E9%99%90" tabindex="-1">清除权限</h3>
<p>因为格式工厂在启动时会自动生成<code>luminati</code>文件夹，并往该文件夹下添加文件。</p>
<p>右键<code>luminati</code>文件夹 -&gt; 点击属性 -&gt; 点击安全 -&gt; 点击高级 -&gt; 点击左下角的禁用继承 -&gt; 点击右下角的应用。</p>
<p>这样操作完之后用户都对这个文件夹没权限了，可以视情况留一个管理员用户。</p>
<h3 id="%E5%88%A0%E9%99%A4%E6%9C%8D%E5%8A%A1" tabindex="-1">删除服务</h3>
<p>打开任务管理器 -&gt; 点击服务 -&gt; 点击底部的打开服务 -&gt; 随便选中一个服务然后按B，会自动跳到第一个以B开头的服务 -&gt; 然后找到BrightData -&gt; 右键该服务 -&gt; 点击属性 -&gt; 复制服务名称</p>
<p>以管理员权限打开cmd，输入</p>
<pre><code class="language-shell">sc delete 刚刚复制的服务名
</code></pre>
<p>按下回车即可删除该服务</p>
<br>
<h2 id="%E8%AF%84%E4%BB%B7" tabindex="-1">评价</h2>
<p>也是够流氓，用户完全不知情的情况下就擅自开启了这东西，要是别人通过自家网络干了什么违法的事，都没地方找人说理去。</p>]]></description><guid isPermaLink="false">/2022/08/17/%E8%AE%B0%E5%BD%95%E7%A6%81%E7%94%A8%E5%B9%B6%E5%88%A0%E9%99%A4%E6%A0%BC%E5%BC%8F%E5%B7%A5%E5%8E%82%E8%87%AA%E5%B8%A6%E7%9A%84brightdata</guid><dc:creator>Administrator</dc:creator><category>Windows</category><category>默认分类</category><pubDate>Wed, 17 Aug 2022 13:20:39 GMT</pubDate></item><item><title><![CDATA[[记录,学习,整理] 在Ubuntu22.04上编译Nginx]]></title><link>https://blog.yingye.site/2022/05/31/zai-ubuntu-shang-bian-yi-nginx</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%2C%E5%AD%A6%E4%B9%A0%2C%E6%95%B4%E7%90%86%5D%20%E5%9C%A8Ubuntu22.04%E4%B8%8A%E7%BC%96%E8%AF%91Nginx&amp;url=/2022/05/31/zai-ubuntu-shang-bian-yi-nginx" width="1" height="1" alt="" style="opacity:0;">
<h2 id="2022-6-6-%E6%9B%B4%E6%96%B0" tabindex="-1">2022-6-6 更新</h2>
<p>在清华大学镜像源里发现了一个给Debian和Ubuntu用的Nginx源：
 <br>
 <a href="https://mirrors.tuna.tsinghua.edu.cn/u.sb/" target="_blank">https://mirrors.tuna.tsinghua.edu.cn/u.sb/</a></p>
<p>Ubuntu 22.04的操作步骤如下：</p>
<pre><code class="language-shell">curl https://n.wtf/public.key -o /usr/share/keyrings/n.wtf.asc
echo "deb [signed-by=/usr/share/keyrings/n.wtf.asc] https://mirrors.tuna.tsinghua.edu.cn/u.sb/ jammy main" &gt; /etc/apt/sources.list.d/n.wtf.list
apt update &amp;&amp; apt install nginx
</code></pre>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F06%2F06%2FuEVCj2H1MmnLDYp.png&amp;size=m" alt="u.sb_1.png">
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F06%2F06%2F9Oyut7PWxDsUIpK.png&amp;size=m" alt="u.sb_2.png">
 <br>
 <br></p>
<h2 id="%E5%89%8D%E8%A8%80" tabindex="-1">前言</h2>
<p>Ubuntu 22.04默认源里的Nginx 1.18貌似有问题，我在一台VPS上安装到最后时最后会报一个dpkg的错误。</p>
<p>这里选择自己编译一个1.22.0版本的Nginx。</p>
<br>
<h2 id="%E6%9E%84%E5%BB%BA" tabindex="-1">构建</h2>
<h3 id="%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96" tabindex="-1">安装依赖</h3>
<pre><code class="language-shell">apt install make gcc libssl-dev libpcre3-dev zlib1g-dev libxml2-dev libxslt1-dev libgd-dev
</code></pre>
<h3 id="%E4%B8%8B%E8%BD%BD%E6%BA%90%E7%A0%81" tabindex="-1">下载源码</h3>
<pre><code class="language-shell">wget https://nginx.org/download/nginx-1.22.0.tar.gz
</code></pre>
<h3 id="%E8%A7%A3%E5%8E%8B" tabindex="-1">解压</h3>
<pre><code class="language-shell">tar -zxf nginx-1.22.0.tar.gz
</code></pre>
<h3 id="%E6%9E%84%E5%BB%BA%E5%8F%82%E6%95%B0" tabindex="-1">构建参数</h3>
<p>编译用的参数从另一台ubuntu 20.04安装的ubuntu 1.18复制的</p>
<pre><code class="language-shell">cd nginx-1.22.0 &amp;&amp; ./configure --with-cc-opt='-g -O2 -fdebug-prefix-map=/build=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \
--with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' \
--prefix=/usr/share/nginx \
--conf-path=/etc/nginx/nginx.conf \
--http-log-path=/var/log/nginx/access.log \
--error-log-path=/var/log/nginx/error.log \
--lock-path=/var/lock/nginx.lock \
--pid-path=/run/nginx.pid \
--modules-path=/usr/lib/nginx/modules \
--http-client-body-temp-path=/var/lib/nginx/body \
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
--http-proxy-temp-path=/var/lib/nginx/proxy \
--http-scgi-temp-path=/var/lib/nginx/scgi \
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
--with-debug \
--with-compat \
--with-pcre-jit \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-http_auth_request_module \
--with-http_v2_module \
--with-http_dav_module \
--with-http_slice_module \
--with-threads \
--with-http_addition_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_image_filter_module=dynamic \
--with-http_sub_module \
--with-http_xslt_module=dynamic \
--with-stream=dynamic \
--with-stream_ssl_module \
--with-mail=dynamic \
--with-mail_ssl_module
</code></pre>
<h3 id="%E7%BC%96%E8%AF%91%E5%B9%B6%E5%AE%89%E8%A3%85" tabindex="-1">编译并安装</h3>
<pre><code class="language-shell">make &amp;&amp; make install
</code></pre>
<br>
<h2 id="%E8%BF%90%E8%A1%8C%E9%85%8D%E7%BD%AE" tabindex="-1">运行配置</h2>
<p>将可执行文件复制到/sbin目录</p>
<pre><code class="language-shell">cp objs/nginx /sbin/
</code></pre>
<p>创建运行时文件夹，nginx运行时会往这里写入文件</p>
<pre><code class="language-shell">mkdir /var/lib/nginx
</code></pre>
<p>创建并启用service，文件路径: /lib/systemd/system/nginx.service</p>
<pre><code class="language-text">[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target
</code></pre>
<pre><code class="language-shell">systemctl enable nginx.service &amp;&amp; service nginx start
</code></pre>]]></description><guid isPermaLink="false">/2022/05/31/zai-ubuntu-shang-bian-yi-nginx</guid><dc:creator>Administrator</dc:creator><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Tue, 31 May 2022 03:53:00 GMT</pubDate></item><item><title><![CDATA[[记录,整理] 使用Ubuntu创建一个求生之路2服务器]]></title><link>https://blog.yingye.site/2022/05/08/%E4%BD%BF%E7%94%A8ubuntu%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%B1%82%E7%94%9F%E4%B9%8B%E8%B7%AF2%E6%9C%8D%E5%8A%A1%E5%99%A8</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20%E4%BD%BF%E7%94%A8Ubuntu%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%B1%82%E7%94%9F%E4%B9%8B%E8%B7%AF2%E6%9C%8D%E5%8A%A1%E5%99%A8&amp;url=/2022/05/08/%E4%BD%BF%E7%94%A8ubuntu%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%B1%82%E7%94%9F%E4%B9%8B%E8%B7%AF2%E6%9C%8D%E5%8A%A1%E5%99%A8" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E7%8E%AF%E5%A2%83" tabindex="-1">环境</h2>
<p>全新安装的Ubuntu 22.04 LTS (GNU/Linux 5.15.0-27-generic x86_64)</p>
<br>
<h2 id="%E5%AE%89%E8%A3%85%E4%BE%9D%E8%B5%96" tabindex="-1">安装依赖</h2>
<pre><code class="language-shell">sudo apt update
sudo apt install lib32gcc-s1 # 看参考文章里是lib32gcc1，但新版本中apt提示替换成lib32gcc-s1了
</code></pre>
<br>
<h2 id="%E5%AE%89%E8%A3%85steamcmd" tabindex="-1">安装Steamcmd</h2>
<pre><code class="language-shell">mkdir ~/steamcmd
cd ~/steamcmd
wget https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz
tar -zxvf steamcmd_linux.tar.gz
./steamcmd.sh
</code></pre>
<br>
<h2 id="%E5%AE%89%E8%A3%85left-4-dead-2" tabindex="-1">安装Left 4 Dead 2</h2>
<p>匿名登录</p>
<pre><code class="language-shell">Steam&gt;login anonymous
</code></pre>
<p>指定下载目录</p>
<pre><code class="language-shell">Steam&gt;force_install_dir /home/当前用户名/l4d2
</code></pre>
<p>下载L4D2服务端</p>
<pre><code class="language-shell">Steam&gt;app_update 222860 validate
</code></pre>
<p>退出Steamcmd</p>
<pre><code class="language-shell">Steam&gt;quit
</code></pre>
<br>
<h2 id="%E5%BC%80%E5%90%AF0-lerp%E5%92%8C128-tick" tabindex="-1">开启0 lerp和128 tick</h2>
<h3 id="%E4%B8%8B%E8%BD%BDsourcemod%E5%92%8Cmetamod" tabindex="-1">下载<a href="https://www.sourcemod.net/downloads.php?branch=stable" target="_blank">SourceMod</a>和<a href="https://www.sourcemm.net/downloads.php" target="_blank">Metamod</a></h3>
<pre><code class="language-shell">cd ~
curl -O https://sm.alliedmods.net/smdrop/1.10/sourcemod-1.10.0-git6538-linux.tar.gz
curl -O https://mms.alliedmods.net/mmsdrop/1.11/mmsource-1.11.0-git1145-linux.tar.gz
tar -zxf sourcemod-1.10.0-git6538-linux.tar.gz
tar -zxf mmsource-1.11.0-git1145-linux.tar.gz
cp -R addons cfg l4d2/left4dead2/
</code></pre>
<h3 id="%E4%B8%8B%E8%BD%BDtickrate-enabler" tabindex="-1">下载<a href="https://github.com/Satanic-Spirit/Tickrate-Enabler" target="_blank">Tickrate-Enabler</a></h3>
<blockquote>
 <p>解压文件，然后分别根据服务器类型参考以下操作：</p>
 <p>1)、win系统服务器：移动tickrate_enabler.dll文件和tickrate_enabler.vdf到left4dead2/addons/目录下 （在addons目录下，新建名为tickrate_enabler的文件夹把tickrate_enabler.dll放进去也可以生效，但tickrate_enabler.vdf必须放在addons根目录下。好处主要是直观，找起来方便）。</p>
 <p>2)、Linux系统服务器(CentOS、Ubuntu之类的)：移动tickrate_enabler.so文件和tickrate_enabler.vdf到left4dead2/addons/目录下 （在addons目录下，新建名为tickrate_enabler的文件夹把tickrate_enabler.so放进去也可以生效，但tickrate_enabler.vdf必须放在addons根目录下。好处主要是直观，找起来方便）。</p>
</blockquote>
<p>但在测试下来Windows服务器上可以不安装这个mod，如果安装了反而会启动不起来。</p>
<h3 id="%E4%BF%AE%E6%94%B9%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6" tabindex="-1">修改配置文件</h3>
<pre><code class="language-shell">nano ~/l4d2/left4dead2/cfg/server.cfg
</code></pre>
<p>配置文件内容，不考虑防D和人数</p>
<pre><code class="language-plains">hostname "local test" // 服务器名称

sv_consistency 0 //防止mod冲突

sm_cvar fps_max 300 //高于推荐的100素质,否则计算刻度会下降
sm_cvar sv_mincmdrate "128" //服务器CMD最小速率
sm_cvar sv_maxcmdrate "128" //服务器CMD最大速率
sm_cvar sv_minupdaterate "128" //服务器更新最小速率
sm_cvar sv_maxupdaterate "128" //服务器更新最大速率
sm_cvar sv_minrate "128000" //设置数据速率传输下限,0=无限制,手动设定30K,表示30tick
sm_cvar sv_maxrate "0" //设置数据速率传输上限.
sm_cvar sv_client_min_interp_ratio 0 //允许客户端设置cl_interp_ratio为任何值，此值为-1时会导致下面那行也就是sv_client_max_interp_ratio设置无效（官方文档说明）
sm_cvar sv_client_max_interp_ratio 2 //当最小值为-1时，本值无效
sm_cvar net_splitrate 1 //每帧可以发送的拆分数据包的片段数,默认为1,调大可能会导致choke增加,改动需谨慎
sm_cvar net_splitpacket_maxrate 100000 //Tickrate_Enabler所需参数 ,大神备注为网络调整，初步估计为配合高tick值的网络调整，官方解释为： Max bytes per second when queueing splitpacket chunks。
sm_cvar nb_update_frequency "0.01" //默认0.1；值越小，小僵尸和女巫的路径以及状态更新越频繁，非常吃服务器的CPU；但如果不设置小一些，客户端开启0 lerp后会出现僵尸和女巫闪烁前进的情况
</code></pre>
<p>运行时注释需去掉，解析时会出现问题，此处注释仅为方便理解。</p>
<br>
<h2 id="%E5%90%AF%E5%8A%A8l4d2%E6%9C%8D%E5%8A%A1%E5%99%A8" tabindex="-1">启动L4D2服务器</h2>
<pre><code class="language-shell">cd ~/l4d2 &amp;&amp; ./srcds_run -game left4dead2 -tickrate 128 +exec server.cfg
</code></pre>
<br>
<h2 id="%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%85%8D%E7%BD%AE" tabindex="-1">客户端配置</h2>
<p>在<code>SteamLibrary/steamapps/common/Left 4 Dead 2/left4dead2/cfg</code>下添加配置文件，这里取名为lerp_0.cfg</p>
<p>内容为</p>
<pre><code class="language-plains">cl_cmdrate "128"
cl_updaterate "128"
cl_interp 0
cl_interp_ratio 0
</code></pre>
<p>在游戏内想开启0 lerp的时候，只需要调出控制台，输入</p>
<pre><code class="language-shell">exec lerp_0
</code></pre>
<p>即可开启</p>
<br>
<h2 id="%E5%85%B6%E4%BB%96" tabindex="-1">其他</h2>
<h3 id="%E7%82%B9%E5%87%BB%E5%8A%A0%E5%85%A5steam%E7%BB%84%E5%8D%B4%E8%B7%B3%E8%BD%AC%E5%88%B0%E9%94%99%E8%AF%AF%E7%9A%84steam%E7%BB%84" tabindex="-1">点击加入Steam组却跳转到错误的Steam组</h3>
<p>下载这个<a href="https://www.sourcemod.net/vbcompiler.php?file_id=175670" target="_blank">插件</a>，放在addons文件夹中</p>
<p>相关讨论：</p>
<p><a href="https://forums.alliedmods.net/showthread.php?t=300683" target="_blank">sv_steamgroup fixer - AlliedModders (alliedmods.net)</a></p>
<p><a href="https://steamcommunity.com/app/550/discussions/0/1736636051411668365/?ctp=1" target="_blank">Dedicated server sv_steamgroup is incorrect :: Left 4 Dead 2 综合讨论 (steamcommunity.com)</a></p>
<br>
<h2 id="%E8%B5%84%E6%96%99%E6%9D%A5%E6%BA%90" tabindex="-1">资料来源</h2>
<p><a href="https://blog.csdn.net/Flowers_for_Algernon/article/details/107501220" target="_blank">Ubuntu下搭建求生之路2服务器 -_Flower_For_Algernon的博客</a></p>
<p><a href="https://blog.csdn.net/ZBzibing/article/details/116126418" target="_blank">L4D2 Tickrate Enabler 服务器速率配置方法_- ZBzibing的博客</a></p>
<p><a href="https://www.bilibili.com/read/cv12184844" target="_blank">求生之路2 服务器设置100tick方法 - 哔哩哔哩 (bilibili.com)</a></p>
<p><a href="https://www.bilibili.com/read/cv12114177" target="_blank">求生之路2 L4D2 服务器网络参数设置 - 哔哩哔哩 (bilibili.com)</a></p>]]></description><guid isPermaLink="false">/2022/05/08/%E4%BD%BF%E7%94%A8ubuntu%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E6%B1%82%E7%94%9F%E4%B9%8B%E8%B7%AF2%E6%9C%8D%E5%8A%A1%E5%99%A8</guid><dc:creator>Administrator</dc:creator><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Sat, 7 May 2022 22:47:23 GMT</pubDate></item><item><title><![CDATA[[记录] 360导致代码执行时间过长]]></title><link>https://blog.yingye.site/2022/05/06/360-dao-zhi-dai-ma-zhi-xing-shi-jian-guo-chang</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20360%E5%AF%BC%E8%87%B4%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%97%B6%E9%97%B4%E8%BF%87%E9%95%BF&amp;url=/2022/05/06/360-dao-zhi-dai-ma-zhi-xing-shi-jian-guo-chang" width="1" height="1" alt="" style="opacity:0;">
<p>同学用Electron写的项目有个需求，要获取局域网内没有被占用的IP。</p>
<p>写了份暴力的代码在本地测试完可行就丢过去了。</p>
<pre><code class="language-javascript">const CP = require("child_process");

new Promise((resolve, reject) =&gt; {
  const exec = CP.exec;
  const wreg = /\d+ms\sTTL\=/;
  const lreg = /icmp\_seq=\d+\sttl=\d+/;
  const ips = [];
  const promises = [];
  console.log(process.platform);
  console.time(1)
  for (let i = 0; i &lt;= 255; i++) {
    promises[i] = new Promise(resolve =&gt; {
      console.timeLog(1, `${i}进入Promise`)
      if (process.platform === "win32") {
        exec(`ping 192.168.1.${i} -4 -n 1 -w 3`, (err, stdout, stderr) =&gt; {
          console.timeLog(1, `${i}完成`)
          const rs = stdout.split("\n").slice(2, 5);
          const flag = rs.some(str =&gt; wreg.test(str));
          if (!flag) {
            ips.push(`192.168.1.${i}`);
          }
          resolve();
        });
      } else {
        exec(`ping 192.168.1.${i} -4 -c 1 -w 3`, (err, stdout, stderr) =&gt; {
          console.timeLog(1, `${i}完成`)
          const rs = stdout.split("\n").slice(1, 2);
          const flag = rs.some(str =&gt; lreg.test(str));
          if (!flag) {
            ips.push(`192.168.1.${i}`);
          }
          resolve();
        });
      }
    });
  }

  Promise.all(promises).then(res =&gt; {
    if (res.length == 256) {
      console.timeEnd(1);
      console.log(ips);
    }
  })
});
</code></pre>
<p>本地测下来4秒左右能获取到所有未占用的ip，但是他那边却需要26秒才能完成。</p>
<p>通过<code>console.timeLog</code>发现他才每次进入Promise函数时所花费的时间特别高，浮动也很高快的时候十几ms，慢的时候几百ms。</p>
<p>不严谨测试数据</p>
<table>
 <thead>
  <tr>
   <th>CPU</th>
   <th>核心数</th>
   <th>系统</th>
   <th>Node版本</th>
   <th>创建耗时</th>
   <th>完成耗时</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <td>Intel Xeon Gold 6133</td>
   <td>2C/2T</td>
   <td>Debian 11(5.10.0-12-amd64)</td>
   <td>v16.14.1</td>
   <td>997.808ms</td>
   <td>4.187s(10.743s)</td>
  </tr>
  <tr>
   <td>AMD Ryzen 7 4800U</td>
   <td>8C/16T</td>
   <td>Windows10 21H1 19043.1645(开启Windows安全中心)</td>
   <td>v16.13.2</td>
   <td>3.965s</td>
   <td>4.141s</td>
  </tr>
  <tr>
   <td>AMD Ryzen 7 4800U</td>
   <td>8C/16T</td>
   <td>Windows10 21H1 19043.1645(关闭Windows安全中心)</td>
   <td>v16.13.2</td>
   <td>3.188s</td>
   <td>3.516s</td>
  </tr>
  <tr>
   <td>Intel Core I5 10400</td>
   <td>6C/12T</td>
   <td>Windows10 20H2 19042.1645(开启360)</td>
   <td>v16.14.0</td>
   <td>23.168S</td>
   <td>26.514s</td>
  </tr>
  <tr>
   <td>Intel Core I5 10400</td>
   <td>6C/12T</td>
   <td>Windows10 20H2 19042.1645(关闭360)</td>
   <td>v16.14.0</td>
   <td>1.853s</td>
   <td>5.628s</td>
  </tr>
  <tr>
   <td>Intel Celeron N3150</td>
   <td>2C/4T</td>
   <td>Debian11(5.11.22-1-pve)</td>
   <td>v16.13.2</td>
   <td>1.701s</td>
   <td>4.763s(10.358s)</td>
  </tr>
 </tbody>
</table>
<p>测试下来，貌似是360做了什么操作，导致用时过长。</p>
<p>Windows安全中心虽然也点影响，但并没有360那么大。</p>]]></description><guid isPermaLink="false">/2022/05/06/360-dao-zhi-dai-ma-zhi-xing-shi-jian-guo-chang</guid><dc:creator>Administrator</dc:creator><category>Windows</category><category>JavaScript</category><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Fri, 6 May 2022 02:26:40 GMT</pubDate></item><item><title><![CDATA[[学习,记录,整理] 使用Flarum简单创建一个论坛系统]]></title><link>https://blog.yingye.site/2022/05/01/%E4%BD%BF%E7%94%A8flarum%E7%AE%80%E5%8D%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%AE%BA%E5%9D%9B%E7%B3%BB%E7%BB%9F</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20%E4%BD%BF%E7%94%A8Flarum%E7%AE%80%E5%8D%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%AE%BA%E5%9D%9B%E7%B3%BB%E7%BB%9F&amp;url=/2022/05/01/%E4%BD%BF%E7%94%A8flarum%E7%AE%80%E5%8D%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%AE%BA%E5%9D%9B%E7%B3%BB%E7%BB%9F" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E8%99%9A%E6%8B%9F%E6%9C%BA%E7%89%88%E6%9C%AC" tabindex="-1">虚拟机版本</h2>
<p>Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-64-generic x86_64)</p>
<h2 id="%E5%9F%BA%E7%A1%80%E7%8E%AF%E5%A2%83" tabindex="-1">基础环境</h2>
<p>LNMP环境的搭建使用军哥的<a href="https://lnmp.org/" target="_blank">LNMP一键安装包</a></p>
<p>Flarum的需求为</p>
<blockquote>
 <ul>
  <li><strong>Apache</strong>（需要启用 mod_rewrite 重写模块) 或 <strong>Nginx</strong></li>
  <li><strong>PHP 7.3+</strong> (需要启用 curl, dom, exif, fileinfo, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip 扩展)</li>
  <li><strong>MySQL 5.6+/8.0.23+</strong> 或 <strong>MariaDB 10.0.5+</strong></li>
 </ul>
</blockquote>
<p>安装时选择的环境为</p>
<blockquote>
 <ul>
  <li><strong>Nginx 1.18</strong></li>
  <li><strong>PHP 7.4</strong></li>
  <li><strong>MariaDB 10.3.23</strong></li>
 </ul>
</blockquote>
<h2 id="%E5%AE%89%E8%A3%85" tabindex="-1">安装</h2>
<h3 id="1.-%E5%AE%89%E8%A3%85composer" tabindex="-1">1. 安装Composer</h3>
<pre><code class="language-shell">php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer
</code></pre>
<h3 id="2.-%E5%AE%89%E8%A3%85flarum" tabindex="-1">2. 安装Flarum</h3>
<p>随意建立一个空文件夹，演示时将文件夹建立在了/www/flarum</p>
<pre><code class="language-shell">mkdir -p /www/flarum &amp;&amp; cd /www/flarum
</code></pre>
<p>cd进去后执行以下命令</p>
<pre><code class="language-shell">composer create-project flarum/flarum .
composer require flarum-lang/chinese-simplified # 提前安装好中文包
</code></pre>
<p>因为要从外网下载依赖，所以这一步要网络的畅通，如果是在虚拟机里面测试，且虚拟机可以连接到宿主机，可以在宿主机上过墙软件中开启socks5</p>
<pre><code class="language-shell">export http_proxy="socks5://宿主机ip:socks5端口" &amp;&amp; export https_proxt="socks5://宿主机ip:socks5端口"
</code></pre>
<h3 id="3.-nginx%E9%85%8D%E7%BD%AE" tabindex="-1">3. nginx配置</h3>
<p>军哥的<a href="https://lnmp.org/" target="_blank">LNMP一键安装包</a>将nginx安装到了<code>/usr/local/nginx</code>，并在配置文件中引入了<code>/usr/local/nginx/conf/vhost</code>文件夹下所有的.conf文件，那么这里就在这个目录下面编写一个配置文件(<strong>这里演示时取的文件名为flarum.conf</strong>)。</p>
<pre><code class="language-properties">server {
    listen 8080;
    server_name flarum;
    index index.php;
    root /www/flarum/public;

    include enable-php.conf;
    include /www/flarum/.nginx.conf;
}
</code></pre>
<p>修改<code>/usr/local/nginx/conf/fastcgi.conf</code></p>
<pre><code class="language-properties">#fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";
fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/:/www/flarum/";
</code></pre>
<p>不修改就访问论坛页面的话，会因为Flarum引用了网站root目录以外的文件而报500。</p>
<p>修改目录所有者，因为执行操作时为了方便，使用的是<code>root</code>；而<a href="https://lnmp.org/" target="_blank">LNMP一键安装包</a>设置的nginx启动用户是<code>www</code>，所以这里还得切换一下目录的所有者，不然也是没法访问的。</p>
<pre><code class="language-shell">chown www:www -R /www/flarum
</code></pre>
<p>配置完就可以去网页安装了。</p>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F05%2F01%2FCTHUmqWb7dukLxX.png&amp;size=m" alt="屏幕截图 2022-05-01 105025.png"></p>
<h3 id="4.-%E8%AE%BE%E7%BD%AE%E4%B8%AD%E6%96%87" tabindex="-1">4. 设置中文</h3>
<p>因为之前已经将中文包安装好了，现在只需要去到网页管理后台开启并将中文设置为默认语言即可。</p>
<p><a href="http://xn--ip-tx5di3ni80c" target="_blank">http://虚拟机ip</a>:nginx端口/admin，演示的虚拟机ip是192.168.1.56，nginx配置的端口是8080，那么这里的访问路径就为http://192.168.1.56:8080/admin</p>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F05%2F01%2FkMtIYiJRwc7j6z9.png&amp;size=m" alt="屏幕截图 2022-05-01 110529.png"></p>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F05%2F01%2F7ZOhVvD1QsIW53f.png&amp;size=m" alt="屏幕截图 2022-05-01 110810.png"></p>]]></description><guid isPermaLink="false">/2022/05/01/%E4%BD%BF%E7%94%A8flarum%E7%AE%80%E5%8D%95%E5%88%9B%E5%BB%BA%E4%B8%80%E4%B8%AA%E8%AE%BA%E5%9D%9B%E7%B3%BB%E7%BB%9F</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2022%2F05%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%25202022-05-01%2520113519.png&amp;size=m" type="image/jpeg" length="0"/><category>默认分类</category><category>Ubuntu</category><pubDate>Sun, 1 May 2022 03:21:42 GMT</pubDate></item><item><title><![CDATA[[学习,记录] 原生JS实现瀑布流]]></title><link>https://blog.yingye.site/2022/01/27/%E7%80%91%E5%B8%83%E6%B5%81</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%5D%20%E5%8E%9F%E7%94%9FJS%E5%AE%9E%E7%8E%B0%E7%80%91%E5%B8%83%E6%B5%81&amp;url=/2022/01/27/%E7%80%91%E5%B8%83%E6%B5%81" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E5%8E%9F%E7%94%9F%E5%AE%9E%E7%8E%B0" tabindex="-1">原生实现</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;

&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
  &lt;title&gt;瀑布流&lt;/title&gt;
  &lt;style&gt;
    ul {
      position: relative;
      margin: 0 auto;
      width: 500px;
    }

    li {
      position: absolute;
      width: 240px;
      list-style: none;
      background-color: #FCC;
    }

    li:nth-of-type(2n) {
      background-color: #FFC;
    }

    li:nth-of-type(3n) {
      background-color: #CCF;
    }

    li:nth-of-type(4n) {
      background-color: #CFF;
    }
  &lt;/style&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;div&gt;
    &lt;ul&gt;
      &lt;li style="height: 400px;"&gt;&lt;/li&gt;
      &lt;li style="height: 1200px;"&gt;&lt;/li&gt;
      &lt;li style="height: 100px;"&gt;&lt;/li&gt;
      &lt;li style="height: 200px;"&gt;&lt;/li&gt;
      &lt;li style="height: 700px;"&gt;&lt;/li&gt;
      &lt;li style="height: 320px;"&gt;&lt;/li&gt;
      &lt;li style="height: 500px;"&gt;&lt;/li&gt;
      &lt;li style="height: 100px;"&gt;&lt;/li&gt;
      &lt;li style="height: 900px;"&gt;&lt;/li&gt;
      &lt;li style="height: 320px;"&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/body&gt;

&lt;script&gt;
  (function (colSpace = 20, bottomSpace = 20) {
    // 外层容器
    const container = document.querySelector("ul");
    // 所有待整理的块
    const blocks = document.querySelectorAll("li");
    // 块宽度
    const blockWidth = (function () {
      if (window.getComputedStyle) {
        return (parseFloat(getComputedStyle(container, null).width) - colSpace) / 2;
      } else {
        return (parseFloat(container.currentStyle.width) - colSpace) / 2;
      }
    })();
    let leftHeight = 0;
    let rightHeight = 0;

    function setToLeft(/** @type {HTMLElement} */ e) {
      let sty;
      if (window.getComputedStyle) {
        sty = getComputedStyle(e, null);
      } else {
        sty = e.currentStyle;
      }
      e.style.left = '0px';
      e.style.top = leftHeight + 'px';
      leftHeight += (parseFloat(sty.height) + bottomSpace);
    }

    function setToRight(/** @type {HTMLElement} */ e) {
      let sty;
      if (window.getComputedStyle) {
        sty = getComputedStyle(e, null);
      } else {
        sty = e.currentStyle;
      }

      e.style.left = (parseFloat(sty.width) + colSpace) + 'px';
      e.style.top = rightHeight + 'px';
      rightHeight += (parseFloat(sty.height) + bottomSpace);

    }

    blocks.forEach(function (e, index) {
      console.log(leftHeight, rightHeight);
      if (leftHeight &lt;= rightHeight) {
        setToLeft(e);
      } else {
        setToRight(e);
      }
      if (leftHeight &gt;= rightHeight) {
        container.style.height = leftHeight + 'px';
      } else {
        container.style.height = rightHeight + 'px';
      }
    })
  })();
&lt;/script&gt;

&lt;/html&gt;
</code></pre>
<h2 id="%E5%8E%9F%E7%94%9F%E7%80%91%E5%B8%83%E6%B5%81%E5%AE%9E%E6%88%98" tabindex="-1">原生瀑布流实战</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="zh-CN"&gt;

&lt;head&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;meta http-equiv="X-UA-Compatible" content="IE=edge"&gt;
  &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
  &lt;title&gt;瀑布流实战&lt;/title&gt;
  &lt;style&gt;
    * {
      margin: 0;
      padding: 0;
    }

    #app {
      position: relative;
      margin: 0 auto;
      width: 90%;
    }

    li {
      display: block;
      width: 100%;
      list-style: none;
    }

    li img {
      display: block;
      width: 100%;
    }
  &lt;/style&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;div id="app"&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/3wNeMhUq2ayWds7.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Iup7gHNKPx5GTjq.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/3wNeMhUq2ayWds7.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Iup7gHNKPx5GTjq.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/3wNeMhUq2ayWds7.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Iup7gHNKPx5GTjq.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/3wNeMhUq2ayWds7.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Iup7gHNKPx5GTjq.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Plu5bckpgsOiXBG.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/Iup7gHNKPx5GTjq.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
    &lt;ul class="block"&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/3wNeMhUq2ayWds7.jpg" /&gt;&lt;/li&gt;
      &lt;li&gt;&lt;img data-src="https://s2.loli.net/2022/01/27/AJy15ouHpjOxIvg.jpg" /&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/div&gt;
&lt;/body&gt;

&lt;script&gt;
  /**
 * 
 * @param {Number} col 瀑布流设置多少列
 * @param {Number} ccolSpace 每列之间空多少位置
 * @param {Number} bottomSpace 每一个块底部留多少位置
 * 
 */
  function waterfall(col = innerWidth &lt; 1145 ? 2 : 3, colSpace = 20, bottomSpace = 20) {
    // 外层容器
    const container = document.querySelector("#app");

    // 所有待整理的块
    const blocks = document.querySelectorAll(".block");

    // 块宽度
    const blockWidth = (function () {
      if (window.getComputedStyle) {
        return (parseFloat(getComputedStyle(container, null).width) - (colSpace * (col - 1))) / col;
      } else {
        return (parseFloat(container.currentStyle.width) - (colSpace * (col - 1))) / col;
      }
    })();

    // 存放每一列的高度
    const heights = [];
    for (let i = 0; i &lt; col; i++) {
      // 每一列初始赋值0
      heights[i] = 0;
    }

    // 设置块的位置
    function setLocation(/** @type {HTMLElement} */ e, /** @type {Number} */ colNumber) {
      /** @type {CSSStyleDeclaration} */
      var sty;
      // 兼容性样式获取写法
      if (window.getComputedStyle) {
        sty = getComputedStyle(e, null);
      } else {
        sty = e.currentStyle;
      }

      // 设置块的left、top、width
      e.style.cssText = `position: absolute; left: ${(blockWidth + colSpace) * colNumber}px; top: ${heights[colNumber]}px; width: ${blockWidth}px;`;

      // 累加列的高度
      heights[colNumber] += (parseFloat(sty.height) + bottomSpace);
    }

    blocks.forEach(function (e, index) {
      // 在数组中寻找最低高度所对应的列
      let isi = heights.findIndex(function (n) {
        // 获取所有列中最低的高度
        return n == Math.min(...heights);
      });
      // 传入要设置位置的块，以及要设置到的列数
      setLocation(e, isi);
    });

    container.style.height = Math.max(...heights) + 'px';
  };

  window.onload = function () {
    const imgs = document.querySelectorAll('#app img');
    let count = 0;
    Array.from(imgs).forEach((/** @type {HTMLImageElement} */ img, index, arr) =&gt; {
      img.src = img.dataset.src;
      img.addEventListener('load', function () {
        count++;
        if (count == arr.length) {
          waterfall(4);
        }
      })
    })
  }
&lt;/script&gt;

&lt;/html&gt;
</code></pre>
<p>这种图片加载方式还挺暴力的，可以使用<a href="https://github.com/desandro/imagesloaded" target="_blank">imagesloaded</a>这个库。</p>]]></description><guid isPermaLink="false">/2022/01/27/%E7%80%91%E5%B8%83%E6%B5%81</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2022%2F01%2F%25E5%25B1%258F%25E5%25B9%2595%25E6%2588%25AA%25E5%259B%25BE%25202022-01-27%2520175031-adfdff5291204e679f3d339f0d1483df.png&amp;size=m" type="image/jpeg" length="0"/><category>JavaScript</category><category>HTML</category><category>默认分类</category><pubDate>Thu, 27 Jan 2022 05:15:11 GMT</pubDate></item><item><title><![CDATA[[学习,记录] Electron+Vue+ffi-napi所遇到的问题]]></title><link>https://blog.yingye.site/2022/01/12/electronvueffi-napi%E6%89%80%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%5D%20Electron%2BVue%2Bffi-napi%E6%89%80%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98&amp;url=/2022/01/12/electronvueffi-napi%E6%89%80%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98" width="1" height="1" alt="" style="opacity:0;">
<h1 id="electron-%2B-vue-%2B-ffi-napi" tabindex="-1">Electron + Vue + ffi-napi</h1>
<h2 id="%E6%9C%AC%E5%9C%B0%E7%8E%AF%E5%A2%83" tabindex="-1">本地环境</h2>
<pre><code class="language-plain">node: 16.13.2-ia32
npm: 8.3.0
node-gyp: 8.4.1
python: 3.10.1
Visual Studio: Visual Studio Comminity 2022
</code></pre>
<pre><code class="language-shell">&gt; type %userprofile%\.npmrc
cache=E:\repository\nodejs\cache
prefix=E:\repository\nodejs\modules

# Electron镜像源
electron_mirror=https://repo.huaweicloud.com/electron/
</code></pre>
<br>
<h2 id="%E9%A1%B9%E7%9B%AE%E4%BE%9D%E8%B5%96" tabindex="-1">项目依赖</h2>
<pre><code class="language-json">{
  "name": "2022-1-12",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "electron:serve": "vue-cli-service electron:serve",
    "electron:build": "vue-cli-service electron:build"
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "ffi-napi": "^4.0.3",
    "ref-napi": "^3.0.3",
    "ref-struct-di": "^1.1.1",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-router": "~4.5.0",
    "@vue/cli-plugin-vuex": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "electron": "^16.0.5",
    "electron-builder": "^22.14.5",
    "sass": "^1.26.5",
    "sass-loader": "^8.0.2",
    "vue-cli-plugin-electron-builder": "^2.1.1",
    "vue-template-compiler": "^2.6.11"
  },
  "browserslist": [
    "&gt; 1%",
    "last 2 versions",
    "not dead"
  ]
}
</code></pre>
<br>
<h2 id="%E9%97%AE%E9%A2%98" tabindex="-1">问题</h2>
<h3 id="node-gyp" tabindex="-1">node-gyp</h3>
<p>在安装ffi-napi的时候会进行编译操作，而在编译时报错了，错误信息提示找不到VS，于是根据报错信息去配置了msvs_version和VCINSTALLDIR等等…，后来才发现，错误信息最后的node-gyp版本不对，本地安装的node-gyp版本是8.4.1，而错误信息中提示的版本是8.3.0。</p>
<p>搜索后发现了和我情况一样的人。<a href="https://github.com/nodejs/node-gyp/issues/2552" target="_blank">https://github.com/nodejs/node-gyp/issues/2552</a></p>
<p>对于Windows给到的解决方案如下</p>
<blockquote>
 <p>Windows is a bit trickier, since <code>npm</code> might be installed to the “Program Files” directory, which needs admin privileges in order to modify on current Windows. Therefore, run the following commands <strong>inside a <code>cmd.exe</code> started with “Run as Administrator”</strong>:</p>
 <p>First we need to find the location of <code>node</code>. If you don’t already know the location that <code>node.exe</code> got installed to, then run:</p>
 <pre><code class="language-">$ where node
</code></pre>
 <p>Now <code>cd</code> to the directory that <code>node.exe</code> is contained in e.g.:</p>
 <pre><code class="language-">$ cd "C:\Program Files\nodejs"
</code></pre>
 <p>If your npm version is <em><strong>7 or 8</strong></em>, do:</p>
 <pre><code class="language-">cd node_modules\npm\node_modules\@npmcli\run-script
</code></pre>
 <p>Else if your npm version is <em><strong>less than 7</strong></em>, do:</p>
 <pre><code class="language-">cd node_modules\npm\node_modules\npm-lifecycle
</code></pre>
 <p>Finish by running:</p>
 <pre><code class="language-">$ npm install node-gyp@latest
</code></pre>
 <p><a href="https://github.com/nodejs/node-gyp/blob/master/docs/Updating-npm-bundled-node-gyp.md" target="_blank">https://github.com/nodejs/node-gyp/blob/master/docs/Updating-npm-bundled-node-gyp.md</a></p>
</blockquote>
<p>但是由于我配置了<code>prefix</code>目录，后来安装的npm@8.3.0版本并不在node所在的文件夹中，所以需要去到prefix目录下的npm文件夹中执行相关命令。</p>
<br>
<h3 id="error%3A-no-native-build-was-found" tabindex="-1">Error: No native build was found</h3>
<p>在引入ffi-napi的时候抛出了以下错误</p>
<pre><code class="language-plain">App threw an error during load
Error: No native build was found for platform=win32 arch=ia32 runtime=electron abi=99 uv=1 libc=glibc node=16.9.1 electron=16.0.5 webpack=true
</code></pre>
<p>解决方案是修改<code>vue.config.js</code>，为<code>vue-cli-plugin-electron-builder</code>添加以下内容</p>
<pre><code class="language-javascript">module.exports = {
  pluginOptions: {
    electronBuilder: {
      externals: ['ffi-napi', 'ref-napi']
    }
  }
}
</code></pre>
<p><a href="https://blog.csdn.net/paopao_wu/article/details/107507225#t20" target="_blank">https://blog.csdn.net/paopao_wu/article/details/107507225#t20</a></p>
<p>如果还是报错，则查看package.json，是不是将ffi-napi和ref-napi安装成开发时依赖了。</p>
<br>
<h3 id="error%3A-dynamic-linking-error%3A-win32-error-193" tabindex="-1">Error: Dynamic Linking Error: Win32 error 193</h3>
<p>实际上这个问题是在我刚试着使用ffi-napi时遇到的，ffi-napi不能加载不同架构的dll，x86只能加载x86，x86_64只能加载x86_64。</p>
<p>解决方案：重新生成dll，或用node-gyp重新编译ffi-napi和ref-napi，但需要注意，node-gyp不能跨架构编译，本地的node若为x64的，则node-gyp的编译目标也只能是x64。</p>
<br>
<h3 id="is-not-a-valid-win32-application" tabindex="-1">is not a valid Win32 application</h3>
<p>这个问题是因为编译出的ffi-napi和ref-napi与electron中的node架构不一样所导致的，目前仅在遇到<code>Win32 error 193</code>后碰到过该问题。</p>
<p>解决方案：重新编译ffi-napi和ref-napi，或更换electron的版本，可以在npm install的时候通过添加<code>--arch=ia32</code>或<code>--arch=x64</code>来指定安装的架构。</p>
<p>不过需要注意的是，如果本地的node为ia32的，那么即使指定了<code>--arch=x64</code>，安装的也会是ia32的版本。</p>
<br>
<h3 id="error%3A-dynamic-linking-error%3A-win32-error-126" tabindex="-1">Error: Dynamic Linking Error: Win32 error 126</h3>
<p><a href="https://www.cnblogs.com/silenzio/p/11606389.html" target="_blank">https://www.cnblogs.com/silenzio/p/11606389.html</a></p>
<p>查看6.1</p>]]></description><guid isPermaLink="false">/2022/01/12/electronvueffi-napi%E6%89%80%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98</guid><dc:creator>Administrator</dc:creator><category>JavaScript</category><category>默认分类</category><pubDate>Wed, 12 Jan 2022 08:38:59 GMT</pubDate></item><item><title><![CDATA[[记录,学习] 半自动配置Java环境(JAVA_HOME)]]></title><link>https://blog.yingye.site/2021/08/19/%E8%AE%B0%E5%BD%95%E5%AD%A6%E4%B9%A0%E5%8D%8A%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83javahome</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%2C%E5%AD%A6%E4%B9%A0%5D%20%E5%8D%8A%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AEJava%E7%8E%AF%E5%A2%83%28JAVA_HOME%29&amp;url=/2021/08/19/%E8%AE%B0%E5%BD%95%E5%AD%A6%E4%B9%A0%E5%8D%8A%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83javahome" width="1" height="1" alt="" style="opacity:0;">
<h2 id="准备">准备</h2>
<p>因为使用到了powershell脚本，而powershell默认是禁止运行脚本的，所以需要先配置一下。
 <br>
 以管理员权限打开powershell，运行以下命令，提示选项输入<code>Y</code>。</p>
<pre><code class="language-powershell">Set-ExecutionPolicy RemoteSigned
</code></pre>
<h2 id="运行">运行</h2>
<p>解除限制后，即可将以下代码保存进一个后缀名为<code>.ps1</code>的文本文件中，然后使用管理员权限运行</p>
<pre><code class="language-powershell"># 获取文件夹路径
[string] $javaPath = Read-Host "请输入你的JDK文件夹路径`n例: C:\Program Files\Java\jdk1.8.0_211`n"

# 设置JAVA_HOME环境变量
setx "JAVA_HOME" $javaPath -M

# 获取原始环境变量, 带%的环境变量, 而不是已经变成完整路径的环境变量
[string] $_originPath = reg query "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /v "Path"
# 进行截取, 去除不需要的部分
$_path = $_originPath[-2]

# 貌似在不同版本的powershell上运行有区别, 有的结果带换行, 有的结果不带换行; 甚至在命令行直接运行和在脚本里运行拿出来的结果也不同
if($_path.GetType().Name -eq "Char" -or $_path.StartsWith(";")) { # 如果Type是Char, 那么就是就是不带换行的结果，这里会拿到一个分号(';')
  Write-Output "go if"
  $_path = $_originPath.Substring(110)
} else {
  Write-Output "go else"
  $_path = $_path.Trim().Substring(25)
}

# 添加到Path，并获取执行结果
[string] $rs = setx -m PATH "$_path;%JAVA_HOME%\bin;%JAVA_HOME%\include;%JAVA_HOME%\jre;%JAVA_HOME%\lib;"

# 如果没有返回结果，大概率是没有用管理员权限运行
if ([String]::IsNullOrEmpty($rs)) {
  Write-Output "请确保你使用的是管理员权限运行"
} else {
  if ($rs.StartsWith("成功")) {
    Write-Output "添加的环境变量需要重启才能生效`n请在保存完数据后再重启"
  }
}

Pause
</code></pre>
<h2 id="为什么是powershell">为什么是Powershell</h2>
<p>这个脚本最初的版本是cmd的，但是发现%path%在cmd中，像%JAVA_HOME%\bin这种相对路径，会变成绝对路径，在cmd中不知道该如何接收reg query查询的结果，而Powershell可以直接用 '=' 等号来接收结果，自然就改用Powershell了。</p>]]></description><guid isPermaLink="false">/2021/08/19/%E8%AE%B0%E5%BD%95%E5%AD%A6%E4%B9%A0%E5%8D%8A%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AEjava%E7%8E%AF%E5%A2%83javahome</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2021%2F12%2Fpowershell%26java-08588d5e23d740d38af7bf009e474ad0.jpg&amp;size=m" type="image/jpeg" length="0"/><category>Windows</category><category>默认分类</category><category>Java</category><pubDate>Thu, 19 Aug 2021 05:48:43 GMT</pubDate></item><item><title><![CDATA[[记录] 生成适用于PaperMC的最小JRE环境]]></title><link>https://blog.yingye.site/2021/08/15/sheng-cheng-shi-yong-yu-papermc-de-zui-xiao-jre-huan-jing</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20%E7%94%9F%E6%88%90%E9%80%82%E7%94%A8%E4%BA%8EPaperMC%E7%9A%84%E6%9C%80%E5%B0%8FJRE%E7%8E%AF%E5%A2%83&amp;url=/2021/08/15/sheng-cheng-shi-yong-yu-papermc-de-zui-xiao-jre-huan-jing" width="1" height="1" alt="" style="opacity:0;">
<h2 id="提示">提示</h2>
<p>该操作不一定适用于安装了插件的Paper服务器，一些插件可能会使用到未被包含的模块，虽然可能性应该不怎么高，但也可以选择构建一个完整的jre。</p>
<h2 id="操作">操作</h2>
<h3 id="11-进入到jdk所在的文件夹">1.1 进入到JDK所在的文件夹</h3>
<p>使用管理员身份打开cmd或powershell，然后进入到JDK的目录例如: C:\Program Files\Java\jdk-16</p>
<pre><code class="language-shell">cd C:\Program Files\Java\jdk-16
</code></pre>
<h3 id="12-生成jre">1.2 生成JRE</h3>
<pre><code class="language-shell">bin\jlink --output jre --add-modules java.base,java.logging,java.management,jdk.unsupported,java.xml,java.desktop,java.naming,java.sql,jdk.zipfs --strip-debug --no-man-pages --no-header-files --compress=2
</code></pre>
<p>--output 用于指定JRE的输出目录。
 <br>
 --add-modules 添加指定的模块到自定义的JRE中，模块间用<code>,</code>分隔。</p>
<h3 id="13-测试运行">1.3 测试运行</h3>
<p>测试用的PaperMC版本为<a href="https://papermc.io/api/v2/projects/paper/versions/1.17.1/builds/186/downloads/paper-1.17.1-186.jar">1.17.1-186</a>
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fi.loli.net%2F2021%2F08%2F15%2FJRGV7gzW6iO5y9c.png&amp;size=m" alt="屏幕截图 2021-08-15 134143.png">
 <br>
 生成出的JRE可以打包带走，在其他机器上只需要解压出来，再配置一下环境变量就可以使用了。</p>
<h2 id="文章参考">文章参考</h2>
<p><a href="https://www.cnblogs.com/roadwide/p/12628773.html">使用jdk的jlink工具生成精简jre</a></p>
<p><a href="https://blog.adoptium.net/2021/10/jlink-to-produce-own-runtime/index.html">creating an equivalent runtime with jlink</a></p>]]></description><guid isPermaLink="false">/2021/08/15/sheng-cheng-shi-yong-yu-papermc-de-zui-xiao-jre-huan-jing</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2021%2F12%2Fpapermc-68e8135e25c849e58348567ffcea5cdd.png&amp;size=m" type="image/jpeg" length="0"/><category>默认分类</category><category>Java</category><pubDate>Sun, 15 Aug 2021 05:46:57 GMT</pubDate></item><item><title><![CDATA[[学习,记录,骚操作] 在openwrt上搭建MC服务器]]></title><link>https://blog.yingye.site/2021/04/26/%E5%9C%A8openwrt%E4%B8%8A%E6%90%AD%E5%BB%BAmc%E6%9C%8D%E5%8A%A1%E5%99%A8</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E9%AA%9A%E6%93%8D%E4%BD%9C%5D%20%E5%9C%A8openwrt%E4%B8%8A%E6%90%AD%E5%BB%BAMC%E6%9C%8D%E5%8A%A1%E5%99%A8&amp;url=/2021/04/26/%E5%9C%A8openwrt%E4%B8%8A%E6%90%AD%E5%BB%BAmc%E6%9C%8D%E5%8A%A1%E5%99%A8" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E5%9C%A8openwrt%E4%B8%8A%E6%90%AD%E5%BB%BAmc%E6%9C%8D%E5%8A%A1%E5%99%A8" tabindex="-1">在openwrt上搭建MC服务器</h2>
<p>一次在TG群水群的时候，偶然得知了openwrt能安装alpine linux的包管理器<code>apk</code>，还因为alpine和openwrt都是使用的musl libc库的原因，能正常使用由apk安装的软件。
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F06%2F05%2FkDxtLrhdoyJf68H.png&amp;size=m" alt="alpine_1.png">
 <br>
 <img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fs2.loli.net%2F2022%2F06%2F05%2FaBmXcfLHbSYVCGw.png&amp;size=m" alt="alpine_2.png"></p>
<br>
<p>先安装apk</p>
<pre><code class="language-shell">opkg update &amp;&amp; opkg install apk alpine-keys alpine-repositories screen
</code></pre>
<p>screen用于新建一个shell窗口，保证在断开连接后，服务器依旧能运行。
 <br>
 <br></p>
<p>更新源，并安装java环境</p>
<pre><code class="language-">apk update &amp;&amp; apk add openjdk11-jre
</code></pre>
<p>要是有特殊需求也可以安装完整的jdk</p>
<pre><code class="language-">apk add openjdk11-jdk
</code></pre>
<p>java安装在<code>/usr/lib/jvm</code>文件夹下，使用<code>du -h</code>命令计算，发现完整的jdk占用了160M的空间，需要注意自己的overlay空间是否足够。
 <br>
 <br></p>
<p>然后找个地方建个文件夹，下载服务器核心，我比较喜欢使用papermc: <a href="https://papermc.io/downloads" target="_blank">https://papermc.io/downloads</a>。
 <br>
 建议把硬盘剩余空间建立成一个分区，然后在那里面开服，防止不知道什么时候服务器相关的文件把overlay空间用完了，导致在web界面修改的配置无法保存。
 <br>
 <br></p>
<p>下载好核心后就可以启动服务器了，这里使用到最开始安装的screen。</p>
<pre><code class="language-shell">screen -S 任意名字 # 创建一个shell窗口
screen -x 上面任意写的名字 # 回到某个已经创建的shell窗口
screen -ls # 查看已创建的shell窗口
</code></pre>
<p>同时按下<code>Ctrl+A+D</code>用于退出当前的screen，并将当前screen放在后台运行。也可以输入<code>exit</code>来退出screen，这种方式会销毁当前的screen。</p>
<br>
<p>简单启动服务器</p>
<pre><code class="language-shell">java -jar 服务器核心.jar
</code></pre>
<p>如果软路由内存1G的话，建议给个750M到800M的内存，不过会占用一部分的虚拟内存，但如果硬盘速度不够快就算了，虚拟内存速度慢会影响软路由的运行；软路由内存2G及以上可以分配1300M及以上的内存。
 <br>
 具体优化参数可以参考MCBBS的这篇帖子<a href="https://www.mcbbs.net/thread-478126-1-1.html" target="_blank">Minecraft服务器优化教程 —— 让多带50%的玩家不再是梦</a>。</p>]]></description><guid isPermaLink="false">/2021/04/26/%E5%9C%A8openwrt%E4%B8%8A%E6%90%AD%E5%BB%BAmc%E6%9C%8D%E5%8A%A1%E5%99%A8</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2021%2F06%2Fhome-hero-1200x600-fcc3a090410e4c5d87acb18f1ef43b73.jpg&amp;size=m" type="image/jpeg" length="0"/><category>Openwrt</category><category>默认分类</category><category>Linux</category><pubDate>Sun, 25 Apr 2021 23:59:20 GMT</pubDate></item><item><title><![CDATA[[学习,记录,整理] 在Jetty中开启HTTP2和SSL]]></title><link>https://blog.yingye.site/2021/03/28/%E5%9C%A8jetty%E4%B8%AD%E5%BC%80%E5%90%AFhttp2%E5%92%8Cssl</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20%E5%9C%A8Jetty%E4%B8%AD%E5%BC%80%E5%90%AFHTTP2%E5%92%8CSSL&amp;url=/2021/03/28/%E5%9C%A8jetty%E4%B8%AD%E5%BC%80%E5%90%AFhttp2%E5%92%8Cssl" width="1" height="1" alt="" style="opacity:0;">
<h2 id="代码">代码</h2>
<p>在build.gradle中添加相关的依赖</p>
<pre><code class="language-gradle">dependencies {
    // 为jetty添加的h2的支持
    implementation group: 'org.eclipse.jetty.http2', name: 'http2-server', version: '9.4.36.v20210114'
    // jetty中http2开启ssl所需的依赖
//    implementation group: 'org.eclipse.jetty', name: 'jetty-alpn-server', version: '9.4.36.v20210114'
    // jdk9之后改用这个
    implementation group: 'org.eclipse.jetty', name: 'jetty-alpn-java-server', version: '9.4.36.v20210114'
}
</code></pre>
<br>
<p>添加配置类对Jetty进行配置，文件名随意，这里我起的名是JettySslConfiguration。</p>
<pre><code class="language-java">package run.halo.app.config;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http2.HTTP2Cipher;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;

@Slf4j
@Configuration
@EnableConfigurationProperties
public class JettySslConfiguration extends AbstractConfiguration implements
    WebServerFactoryCustomizer&lt;JettyServletWebServerFactory&gt; {

    @Value("${server.port}")
    private int https;
    @Value("${server.ssl.port}")
    private int http;
    @Value("${jetty.connect.maxThreads}")
    private int maxThread;
    @Value("${jetty.connect.minThreads}")
    private int minThread;
    @Value("${jetty.connect.idleTimeout}")
    private int idleTimeout;

    @Override
    public void configure(WebAppContext context) throws Exception {
        Constraint constraint = new Constraint();
        constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);

        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setPathSpec("/*");
        mapping.setConstraint(constraint);

        ConstraintSecurityHandler handler = new ConstraintSecurityHandler();
        handler.addConstraintMapping(mapping);

        context.setSecurityHandler(handler);
    }

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        factory.setConfigurations(Collections.singleton(this));

        factory.addServerCustomizers(
            server -&gt; {
                // 配置线程池
                threadPool(server);

                // 获取服务器的默认连接器
                ServerConnector connector = (ServerConnector) server.getConnectors()[0];

                // 获取SSL上下文工厂
                SslContextFactory sslContextFactory =
                    connector.getConnectionFactory(SslConnectionFactory.class)
                        .getSslContextFactory();
                // 设置SSL密码对比器
                sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);

                // 获取HTTP配置对象
                HttpConfiguration httpConfiguration =
                    connector.getConnectionFactory(HttpConnectionFactory.class)
                        .getHttpConfiguration();

                // 创建SSL连接工厂
                SslConnectionFactory sslConnectionFactory =
                    new SslConnectionFactory(sslContextFactory, "alpn");

                // 创建ALPN连接工厂，ALPN是TLS的扩展，HTTP2需要使用ALPN来开启SSL
                ALPNServerConnectionFactory alpnServerConnectionFactory =
                    new ALPNServerConnectionFactory("h2", "h2-17", "h2-16", "h2-15", "h2-14");

                // 创建HTTP2连接工厂
                HTTP2ServerConnectionFactory http2ServerConnectionFactory =
                    new HTTP2ServerConnectionFactory(httpConfiguration);

                // 创建用于SSL连接的服务器连接器
                ServerConnector httpsConnector =
                    new ServerConnector(server, sslConnectionFactory, alpnServerConnectionFactory,
                        http2ServerConnectionFactory);
                // 设置HTTPS的监听端口
                httpsConnector.setPort(https);

                // 创建用于HTTP连接的服务器连接器
                ServerConnector httpConnector = new ServerConnector(server);
                // 用HTTPS的配置创建一个HTTP的连接工厂,之后通过HTTP的端口访问会重定向到HTTPS的端口
                httpConnector.addConnectionFactory(new HttpConnectionFactory(httpConfiguration));
                // 设置HTTP的监听端口
                httpConnector.setPort(http);

                // 将HTTP和HTTPS的连接器添加(注册)到服务器中
                server.setConnectors(new Connector[] {httpConnector, httpsConnector});
            }
        );
    }

    private void threadPool(Server server) {
        // 线程池
        final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
        // 设置最大线程连接数
        threadPool.setMaxThreads(maxThread);
        // 设置最小线程连接数
        threadPool.setMinThreads(minThread);
        // 设置线程最大空闲时间60000ms
        threadPool.setIdleTimeout(idleTimeout);
    }

}
</code></pre>
<br>
<p>在编译前或运行前，往<code>application.yaml</code>中添加以下配置。</p>
<pre><code class="language-yaml">server:
  # https端口
  port: 8443
  # http端口
  ssl:
    port: 8080
jetty:
  connect:
    maxThreads: 150
    minThreads: 10
    idleTimeout: 30000
</code></pre>
<p>上面的配置看着挺让人迷惑，但这是由一个问题造成的。
 <br>
 <br>
 <br>
 如果将<code>application.yaml</code>改成这样</p>
<pre><code class="language-yaml">server:
  # http端口
  port: 80
  # https端口
  ssl:
    port: 443
</code></pre>
<p>java代码改成这样</p>
<pre><code class="language-java">@Value("${server.port}")
private int http;
@Value("${server.ssl.port}")
private int https;
</code></pre>
<p>那么在<code>http://127.0.0.1:80</code>重定向时会出问题，会重定向到<code>https://127.0.0.1:80</code>而不是<code>https://127.0.0.1:443</code>。</p>
<p>为了解决这个问题，才让<code>application.yaml</code>中的配置看起来像是人类迷惑行为。当然，也可以不使用Jetty监听HTTP做重定向，可以使用Nginx来监听HTTP再进行重定向，这样就不会有迷惑行为了。</p>
<br>
<p>要开启SSL，肯定得加载证书，在部署时，往<code>~/.halo/application.yaml</code>中添加以下配置</p>
<pre><code class="language-yaml">server:
  ssl:
    key-store: .halo/证书文件
    key-store-password: 证书密码
    keyStoreType: PKCS12
</code></pre>
<p>至于为什么不用Ningx反代来开启SSL...这是个痛点，我的服务器提供商不允许建站机器涉及任何代理行为，反代也在其中。</p>
<br>
<h2 id="文章参考">文章参考</h2>
<p><a href="https://www.dazhuanlan.com/2019/10/05/5d97f05365637/">关于Spring Boot中使用Jetty容器开启HTTP2的配置</a></p>]]></description><guid isPermaLink="false">/2021/03/28/%E5%9C%A8jetty%E4%B8%AD%E5%BC%80%E5%90%AFhttp2%E5%92%8Cssl</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2021%2F06%2F%25E6%2589%25B9%25E6%25B3%25A8%25202021-06-13%2520141544-ac24cc1de10c4ef489d7d6020055b671.png&amp;size=m" type="image/jpeg" length="0"/><category>Spring</category><category>默认分类</category><category>Java</category><pubDate>Sun, 28 Mar 2021 13:13:47 GMT</pubDate></item><item><title><![CDATA[[学习,整理] 无法连接到mariadb/mysql]]></title><link>https://blog.yingye.site/2021/03/22/%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E5%88%B0mariadb%E5%92%8Cmysql</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E6%95%B4%E7%90%86%5D%20%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E5%88%B0mariadb%2Fmysql&amp;url=/2021/03/22/%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E5%88%B0mariadb%E5%92%8Cmysql" width="1" height="1" alt="" style="opacity:0;">
<h2 id="%E6%83%85%E5%86%B5" tabindex="-1">情况</h2>
<p>测试环境为Ubuntu Server 20.04.1 LTS，在使用军哥的<a href="https://lnmp.org/" target="_blank">lnmp脚本</a>搭建完lnmp环境并重启后，发现远程连接不上mariadb。</p>
<pre><code class="language-shell">MariaDB [(none)]&gt; use mysql;
Database changed
MariaDB [mysql]&gt; select user,host from user;
+------+-----------+
| user | host      |
+------+-----------+
| root | 127.0.0.1 |
| root | ::1       |
| root | localhost |
+------+-----------+
4 rows in set (0.000 sec)

MariaDB [mysql]&gt;
</code></pre>
<p>执行查询后发现没有host为<code>%</code>的root用户。于是使用<code>grant</code>命令创建了用于远程登录的<code>root</code>用户。</p>
<pre><code class="language-shell">MariaDB [(none)]&gt; GRANT ALL PRIVILEGES ON *.* TO 新用户名@"%" IDENTIFIED BY '密码' WITH GRANT OPTION;
Query OK, 0 rows affected (0.001 sec)

MariaDB [(none)]&gt; FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.000 sec)
</code></pre>
<p>但使用<code>navicat</code>和<code>heidisql</code>还是无法连接。</p>
<p>于是看了一下端口情况。</p>
<pre><code class="language-bash">root@vuw:~# iptables -L -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:3306
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8
</code></pre>
<p>发现3306端口被<code>DROP</code>了。</p>
<br>
<h2 id="%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88" tabindex="-1">解决方案</h2>
<p>创建一个文件，里面写上要放行的端口
 <br>
 文件路径随意，这里只作为参考：/etc/iptables.rules</p>
<pre><code class="language-bash">*filter
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 3306 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 8080 -j ACCEPT
COMMIT
</code></pre>
<p>第一行必须为<code>*filter</code>，最后一行必须为<code>COMMIT</code>。</p>
<br>
<p>再创建一个脚本，用于将规则导入iptables。
 <br>
 文件路径随意，这里只作为参考：/etc/init.d/port</p>
<pre><code class="language-bash">#!/bin/bash
iptables-restore &lt; /etc/iptables.rules
</code></pre>
<p>赋予脚本执行权限。</p>
<pre><code class="language-shell">chmod +x /etc/init.d/port
</code></pre>
<br>
<p>添加一个service，在网络启动后，对端口进行放行。
 <br>
 /etc/systemd/system/AllowPort.service</p>
<pre><code class="language-">[Unit]
Description=allow port
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
# 执行那个脚本
ExecStart=/etc/init.d/port
# 直接这样写会报错
#ExecStart=iptables-restore &lt; /etc/iptables.rules

[Install]
WantedBy=multi-user.target
</code></pre>
<p>再将这个service设置为开机启动。</p>
<pre><code class="language-bash">systemctl enable AllowPort
</code></pre>
<p>然后就可以重启，使用<code>iptables -L -n</code>命令验收成果了。
 <br>
 在自己解决完问题后去又去军哥的lnmp官网看了一下，在常见问题中就有这个问题的解决方案https://bbs.vpser.net/thread-13563-1-1.html</p>
<br>
<h2 id="%E6%96%87%E7%AB%A0%E5%BC%95%E7%94%A8" tabindex="-1">文章引用</h2>
<p><a href="https://blog.csdn.net/qq_19004627/article/details/100937378" target="_blank">ubuntu18.04 使用systemd方式添加开机运行sh脚本</a>
 <br>
 <a href="https://www.piikee.net/1257.html" target="_blank">ubuntu下设置iptables方法</a></p>]]></description><guid isPermaLink="false">/2021/03/22/%E6%97%A0%E6%B3%95%E8%BF%9E%E6%8E%A5%E5%88%B0mariadb%E5%92%8Cmysql</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fmariadb.org%2Fwp-content%2Fthemes%2Ftwentynineteen-child%2Ficons%2Flogo_seal.svg&amp;size=m" type="image/jpeg" length="5586"/><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Mon, 22 Mar 2021 10:12:53 GMT</pubDate></item><item><title><![CDATA[[学习,记录] ViewModelProvider]]></title><link>https://blog.yingye.site/2021/03/18/viewmodelprovider</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%5D%20ViewModelProvider&amp;url=/2021/03/18/viewmodelprovider" width="1" height="1" alt="" style="opacity:0;">
<h2 id="序言">序言</h2>
<p>在正常情况下，旋转屏幕或切换系统语言，都会导致<code>Activity</code>被销毁，这样页面上原本的数据就会丢失。
 <br>
 ViewModelProvider可以用来保存页面上的数据，但存在一些小问题(我的小问题)。</p>
<h2 id="环境">环境</h2>
<p>IDEA2020.3.3 社区版
 <br>
 java version "1.8.0_271"
 <br>
 Android SDK 7.0 (API24)
 <br>
 项目模板 Empty Activity</p>
<h2 id="代码">代码</h2>
<p>MyViewModel</p>
<pre><code class="language-java">package com.example.viewmodel;

import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {

  public int number = 0;

}
</code></pre>
<p>MainActivity</p>
<pre><code class="language-java">package com.example.viewmodel;

import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.lifecycle.ViewModelProvider;

public class MainActivity extends AppCompatActivity {

  private MyViewModel myViewModel;
  private TextView textView;
  private Button button1, button2;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    myViewModel = new ViewModelProvider.AndroidViewModelFactory(getApplication()).create(MyViewModel.class);

    textView = findViewById(R.id.textView);
    textView.setText(String.valueOf(myViewModel.number));

    button1 = findViewById(R.id.button);
    button2 = findViewById(R.id.button2);

    button1.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        myViewModel.number++;
        textView.setText(String.valueOf(myViewModel.number));
      }
    });
    button2.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        myViewModel.number += 2;
        textView.setText(String.valueOf(myViewModel.number));
      }
    });
  }

}
</code></pre>
<h2 id="界面展示">界面展示</h2>
<p><img src="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fi.loli.net%2F2021%2F03%2F18%2FbUQWOLdpegGukSI.png&amp;size=m" alt="Screenshot_1616059763.png"></p>
<h2 id="坑">坑</h2>
<p>在使用IDEA创建项目时，并没有androidx的选项，而看的教学视频是有的，想着可能是新版会自动引入就finish了，在编写代码时发现确实有<code>ViewModelProvider</code>这个类。
 <br>
 <br></p>
<p>尝试过使用以下两种方式创建<code>MyViewModel</code>的实例</p>
<pre><code class="language-java">myViewModel = new ViewModelProvider.NewInstanceFactory().create(MyViewModel.class);
// 或
myViewModel = new ViewModelProvider.AndroidViewModelFactory(getApplication()).create(MyViewModel.class);
</code></pre>
<p>虽然能正常创建，但都无法让ViewModelProvider保存数据。
 <br>
 <br></p>
<p>根据视频导入了<code>Android Lifecycle ViewModel</code>
 <br>
 build.gradle(app下的)</p>
<pre><code class="language-gradle">// ...
dependencies {
    // ...
    implementation 'androidx.lifecycle:lifecycle-viewmodel:2.3.0'
}
</code></pre>
<p>再使用上面的两种方式，依旧无法保存数据。
 <br>
 <br></p>
<p>于是开始尝试使用弹幕中提到的写法来创建<code>MyViewModel</code>的实例。尝试过使用以下三种写法，都可以保存数据。</p>
<pre><code class="language-java">myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
// 或
myViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
// 或
myViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);
</code></pre>
<p>虽然视频中有讲到按下返回键也能保存，但却未进行演示，个人实验是无法保存的。
 <br>
 视频链接: <a href="https://www.bilibili.com/video/BV1w4411t7UQ">https://www.bilibili.com/video/BV1w4411t7UQ</a></p>]]></description><guid isPermaLink="false">/2021/03/18/viewmodelprovider</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=https%3A%2F%2Fblog.yingye.site%2Fupload%2F2021%2F06%2FAndroid-62086624df504086a1fe8e66a0891fc8.ico&amp;size=m" type="image/jpeg" length="0"/><category>Android</category><category>默认分类</category><category>Java</category><pubDate>Thu, 18 Mar 2021 09:38:26 GMT</pubDate></item><item><title><![CDATA[[学习,记录,整理] halo在不使用nginx进行反代的情况下开启ssl]]></title><link>https://blog.yingye.site/2021/01/31/halo%E5%9C%A8%E4%B8%8D%E4%BD%BF%E7%94%A8nginx%E8%BF%9B%E8%A1%8C%E5%8F%8D%E4%BB%A3%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E5%BC%80%E5%90%AFssl</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%2C%E6%95%B4%E7%90%86%5D%20halo%E5%9C%A8%E4%B8%8D%E4%BD%BF%E7%94%A8nginx%E8%BF%9B%E8%A1%8C%E5%8F%8D%E4%BB%A3%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E5%BC%80%E5%90%AFssl&amp;url=/2021/01/31/halo%E5%9C%A8%E4%B8%8D%E4%BD%BF%E7%94%A8nginx%E8%BF%9B%E8%A1%8C%E5%8F%8D%E4%BB%A3%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E5%BC%80%E5%90%AFssl" width="1" height="1" alt="" style="opacity:0;">
<h2 id="起因">起因</h2>
<p>事情起因是我所购买的建站机，不允许用于代理和使用反代，这样我就没有了使用nginx进行反代的机会。
 <br>
 想在不使用反代的情况下开启ssl，需要自行拉取halo源码并进行一些修改。修改方式有两种。</p>
<ul>
 <li>对自带的<code>jetty</code>进行配置</li>
 <li>使用<code>tomcat</code>替换自带的<code>jetty</code>，对tomcat进行配置</li>
</ul>
<h2 id="tomcat">Tomcat</h2>
<p>使用tomcat需要在<code>build.gradle</code>的dependencies中添加以下依赖</p>
<blockquote>
 <p>compile group: "org.apache.tomcat.embed", name: "tomcat-embed-core", version: "9.0.39"</p>
</blockquote>
<p>再添加一个文件，文件名随意，这里我起的名是<code>TomcatSslConfiguration.java</code>。</p>
<pre><code class="language-java">package run.halo.app.config;

import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableConfigurationProperties
public class TomcatSslConfiguration {

    @Value("${server.port}")
    private int https;
    @Value("${server.ssl.port}")
    private int http;

    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
            @Override
            protected void postProcessContext(Context context) {
                SecurityConstraint constraint = new SecurityConstraint();
                constraint.setUserConstraint("CONFIDENTIAL");
                SecurityCollection collection = new SecurityCollection();
                // 全路径重定向到https
                collection.addPattern("/*");
                constraint.addCollection(collection);
                context.addConstraint(constraint);
            }
        };
        tomcat.addAdditionalTomcatConnectors(httpConnector());
        return tomcat;
    }

    @Bean
    public Connector httpConnector() {
        if (http == 0) {
            http = 8090;
        }
        if (https == 0) {
            https = 8443;
        }
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        // 设置用什么方案接收请求
        connector.setScheme("http");
        // 设置要监听的端口号
        connector.setPort(http);
        // 开启http重定向到https
        connector.setSecure(false);
        // 重定向到哪个端口
        connector.setRedirectPort(https);
        return connector;
    }

}
</code></pre>
<p>然后使用<code>Tasks &gt; build &gt; build</code>进行编译，虽然会有过时待删除的警告，但在<code>build &gt; lib</code>文件夹中能看到编译出的jar包。
 <br>
 然后去下载适用于tomcat的证书文件，推荐将证书文件放在<code>.halo</code>文件夹下，部署的时候在<code>.halo/application.yaml</code>中添加以下配置。</p>
<pre><code class="language-yaml">server:
  # https端口
  port: 443
  ssl:
    # http端口
    port: 80
    key-store: .halo/证书文件
    key-store-password: 证书密码
    keyStoreType: PKCS12
</code></pre>
<p>配置完即可启动自己编译出的halo检验成果。以上操作相当于直接往SpringBoot中塞了一个tomcat，用来替换原来的web服务器，并在tomcat中进行ssl相关的配置。</p>
<h3 id="文章引用">文章引用</h3>
<p><a href="https://cloud.tencent.com/developer/article/1515468">Spring Boot配置ssl证书启用HTTPS协议</a>
 <br>
 <a href="https://www.cnblogs.com/TheoryDance/p/11180023.html">springboot配置ssl证书</a>
 <br>
 <a href="https://www.cnblogs.com/garyyan/p/7600322.html">Https系列之三：让服务器同时支持http、https，基于spring boot</a></p>
<br>
<h2 id="jetty">Jetty</h2>
<p>halo使用的SpringBoot，默认的容器是<code>Jetty</code>，不需要额外导入依赖，仅需添加一个配置类。
 <br>
 文件名随意，这里我起名为<code>JettySslConfiguration.java</code>。</p>
<pre><code class="language-java">package run.halo.app.config;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.security.Constraint;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;

@Slf4j
@Configuration
@EnableConfigurationProperties
public class JettySslConfiguration extends AbstractConfiguration implements
    WebServerFactoryCustomizer&lt;JettyServletWebServerFactory&gt; {

    @Value("${server.port}")
    private int https;
    @Value("${server.ssl.port}")
    private int http;
    @Value("${jetty.connect.maxThreads}")
    private int maxThread;
    @Value("${jetty.connect.minThreads}")
    private int minThread;
    @Value("${jetty.connect.idleTimeout}")
    private int idleTimeout;

    @Override
    public void configure(WebAppContext context) throws Exception {
        Constraint constraint = new Constraint();
        constraint.setDataConstraint(Constraint.DC_CONFIDENTIAL);

        ConstraintMapping mapping = new ConstraintMapping();
        mapping.setPathSpec("/*");
        mapping.setConstraint(constraint);

        ConstraintSecurityHandler handler = new ConstraintSecurityHandler();
        handler.addConstraintMapping(mapping);

        context.setSecurityHandler(handler);
    }

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        factory.setConfigurations(Collections.singleton(this));

        factory.addServerCustomizers(
            server -&gt; {
                // 配置线程池
                threadPool(server);

                HttpConfiguration httpsConfiguration = new HttpConfiguration();
                httpsConfiguration.setSecurePort(https);
                httpsConfiguration.setSecureScheme("https");

                ServerConnector connector = new ServerConnector(server);
                connector.addConnectionFactory(new HttpConnectionFactory(httpsConfiguration));
                connector.setPort(http);

                server.addConnector(connector);
            }
        );
    }

    private void threadPool(Server server) {
        // 线程池
        final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
        //默认最大线程连接数200
        threadPool.setMaxThreads(maxThread);
        //默认最小线程连接数8
        threadPool.setMinThreads(minThread);
        //默认线程最大空闲时间60000ms
        threadPool.setIdleTimeout(idleTimeout);
    }

}
</code></pre>
<p>在不配置<code>threadPool</code>的情况下，部署配置与tomcat的相同，如果配置了<code>threadPool</code>，则还需要修改<code>application.yaml</code>，在编译前/运行前添加以下配置。</p>
<pre><code class="language-yaml">jetty:
  connect:
    # 最大线程数
    maxThreads: 150
    # 最小线程数
    minThreads: 10
    # 线程最大空闲时间
    idleTimeout: 30000
</code></pre>
<br>
<h3 id="文章引用-1">文章引用</h3>
<p><a href="https://zhuanlan.zhihu.com/p/137666602">Springboot以Jetty为容器实现http重定向到https</a>
 <br>
 <a href="https://blog.csdn.net/ming19951224/article/details/85135451">springboot使用tomcat或jetty开通http,https端口</a></p>]]></description><guid isPermaLink="false">/2021/01/31/halo%E5%9C%A8%E4%B8%8D%E4%BD%BF%E7%94%A8nginx%E8%BF%9B%E8%A1%8C%E5%8F%8D%E4%BB%A3%E7%9A%84%E6%83%85%E5%86%B5%E4%B8%8B%E5%BC%80%E5%90%AFssl</guid><dc:creator>Administrator</dc:creator><enclosure url="https://blog.yingye.site/apis/api.storage.halo.run/v1alpha1/thumbnails/-/via-uri?uri=http%3A%2F%2Fnginx.org%2Fnginx.png&amp;size=m" type="image/jpeg" length="2103"/><category>Spring</category><category>默认分类</category><category>Java</category><pubDate>Sun, 31 Jan 2021 10:48:56 GMT</pubDate></item><item><title><![CDATA[[记录] 解决docker中的mariadb连接速度慢]]></title><link>https://blog.yingye.site/2021/01/23/mariadb%E8%BF%9E%E6%8E%A5%E9%80%9F%E5%BA%A6%E6%85%A2</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20%E8%A7%A3%E5%86%B3docker%E4%B8%AD%E7%9A%84mariadb%E8%BF%9E%E6%8E%A5%E9%80%9F%E5%BA%A6%E6%85%A2&amp;url=/2021/01/23/mariadb%E8%BF%9E%E6%8E%A5%E9%80%9F%E5%BA%A6%E6%85%A2" width="1" height="1" alt="" style="opacity:0;">
<p>下面是mysql:8.0容器的配置</p>
<pre><code class="language-ini">[client]
default-character-set=utf8

[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
explicit_defaults_for_timestamp=true
innodb_flush_log_at_trx_commit=0
sync_binlog=0
# 设置最大连接数
max_connections=512
# 跳过对客户端进行DNS反向解析
skip-name-resolve

[mysql]
# 设置默认字符集
default-character-set=utf8
# 跳过对客户端进行DNS反向解析
skip-name-resolve
</code></pre>
<p>因为mariadb是基于mysql，就理所应当的将mysql:8.0的配置抄了过来给mariadb:10.5.8使用，尝试解决连接很慢的问题，结果却一直无法连接。</p>
<p>进入mariadb容器后，尝试使用：<code>mysql -uroot</code> 进入mysql，但却提示了一个错误：<code>mysql: unknown option '--skip-name-resolve'</code></p>
<p>在搜索后找到了解决方法：<a href="https://forums.cpanel.net/threads/mysqldump-error-unknown-option-skip-name-resolve.634289/">unknown option '--skip-name-resolve'</a></p>
<p>将以下配置移除</p>
<pre><code class="language-ini">[mysql]
skip-name-resolve
</code></pre>
<p>并添加以下配置</p>
<pre><code class="language-ini">[mysqldump]
skip_name_resolve
</code></pre>
<p>成功解决每次连接都要十来秒的问题</p>
<br>
<pre><code class="language-ini">[client]
default-character-set=utf8

[mysqld]
character-set-server=utf8
collation-server=utf8_general_ci
explicit_defaults_for_timestamp=true
innodb_flush_log_at_trx_commit=0
sync_binlog=0
# 设置最大连接数
max_connections=512

[mysql]
# 设置默认字符集
default-character-set=utf8
</code></pre>
<p>在解决了mariadb的问题后，尝试修改了mysql:8.0的配置，结果发现mysql:8.0不添加<code>skip-name-resolve</code>也能非常快的连接。</p>
<p>mariadb:10.5.8的配置抄的mysql:8.0的，mysql:8.0的配置又是从mysql:5.7那抄的，<code>skip-name-resolve</code>是当时为了解决mysql:5.7连接慢而添加的。还是因为懒，直接照抄配置带来的问题。</p>]]></description><guid isPermaLink="false">/2021/01/23/mariadb%E8%BF%9E%E6%8E%A5%E9%80%9F%E5%BA%A6%E6%85%A2</guid><dc:creator>Administrator</dc:creator><category>默认分类</category><category>Linux</category><pubDate>Sat, 23 Jan 2021 04:54:17 GMT</pubDate></item><item><title><![CDATA[[记录] Ubuntu关闭IPV6]]></title><link>https://blog.yingye.site/2021/01/15/ubuntu%E5%85%B3%E9%97%ADipv6</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20Ubuntu%E5%85%B3%E9%97%ADIPV6&amp;url=/2021/01/15/ubuntu%E5%85%B3%E9%97%ADipv6" width="1" height="1" alt="" style="opacity:0;">
<p>在Ubuntu18.04中可以通过在<code>/etc/sysctl.conf</code>文件中添加以下语句禁用IPV6</p>
<pre><code class="language-shell">net.ipv6.conf.all.disable_ipv6 = 1
</code></pre>
<p>再执行<code>sysctl -p</code>让更改生效，从而禁用IPV6。
 <br>
 在Ubuntu18.04这很有效，但在Ubuntu20.04不好使了，这种操作变成了临时的，一旦重启就失效了。</p>
<p>通过百度找到了另一种禁用IPV6的方式，修改GRUB的配置文件<code>/etc/default/grub</code>，在其中添加以下语句</p>
<pre><code class="language-shell">GRUB_CMDLINE_LINUX="ipv6.disable=1"
</code></pre>
<p>大部分情况下<code>GRUB_CMDLINE_LINUX</code>是存在且有内容的，那么就在里面追加即可，用空格进行分隔。</p>
<p>然后再执行<code>sudo update-grub</code>，更新GRUB的配置</p>
<p>参考文章：<a href="http://ipv6.infosws.cn/20190702/23750.html">在Ubuntu中完全禁用IPV6的两种方式</a></p>]]></description><guid isPermaLink="false">/2021/01/15/ubuntu%E5%85%B3%E9%97%ADipv6</guid><dc:creator>Administrator</dc:creator><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Fri, 15 Jan 2021 05:40:50 GMT</pubDate></item><item><title><![CDATA[[学习,记录] 在SpringBoot中使用Swagger2]]></title><link>https://blog.yingye.site/2020/11/19/%E5%9C%A8springboot%E4%B8%AD%E4%BD%BF%E7%94%A8swagger2</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E5%AD%A6%E4%B9%A0%2C%E8%AE%B0%E5%BD%95%5D%20%E5%9C%A8SpringBoot%E4%B8%AD%E4%BD%BF%E7%94%A8Swagger2&amp;url=/2020/11/19/%E5%9C%A8springboot%E4%B8%AD%E4%BD%BF%E7%94%A8swagger2" width="1" height="1" alt="" style="opacity:0;">
<h2 id="配置pom.xml">配置pom.xml</h2>
<p>在pom.xml中引入</p>
<ul>
 <li>springfox-boot-starter</li>
 <li>springfox-swagger2</li>
 <li>springfox-swagger-ui</li>
</ul>
<p>这里使用的版本都为3.0.0</p>
<pre><code class="prism language-xml">        &lt;dependency&gt;
            &lt;groupId&gt;io.springfox&lt;/groupId&gt;
            &lt;artifactId&gt;springfox-boot-starter&lt;/artifactId&gt;
            &lt;version&gt;3.0.0&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;io.springfox&lt;/groupId&gt;
            &lt;artifactId&gt;springfox-swagger2&lt;/artifactId&gt;
            &lt;version&gt;3.0.0&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;io.springfox&lt;/groupId&gt;
            &lt;artifactId&gt;springfox-swagger-ui&lt;/artifactId&gt;
            &lt;version&gt;3.0.0&lt;/version&gt;
        &lt;/dependency&gt;
</code></pre>
<h2 id="java配置">java配置</h2>
<pre><code class="prism language-java">@Configuration
@EnableSwagger2
public class SwaggerConfig {

  @Bean
  public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            // 配置扫描指定的包
            .apis(RequestHandlerSelectors.basePackage("com.project.controller"))
            // 扫描所有包
            // .apis(RequestHandlerSelectors.any())
            .paths(PathSelectors.any())
            .build();
  }

  @Bean
  public WebMvcConfigurer SwaggerWebMvcConfigurer() {
    return new WebMvcConfigurer() {
      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
      }
    };
  }

}
</code></pre>
<h2 id="验证成果">验证成果</h2>
<p><a href="http://localhost:8080/your-app-root/swagger-ui/">http://localhost:8080/your-app-root/swagger-ui/</a>
 <br>
 更多配置请参考 <a href="https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api">https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api</a></p>]]></description><guid isPermaLink="false">/2020/11/19/%E5%9C%A8springboot%E4%B8%AD%E4%BD%BF%E7%94%A8swagger2</guid><dc:creator>Administrator</dc:creator><category>Spring</category><category>默认分类</category><category>Java</category><pubDate>Thu, 19 Nov 2020 05:12:17 GMT</pubDate></item><item><title><![CDATA[[记录] Ubuntu18.04 搭建FastDFS分布式存储系统]]></title><link>https://blog.yingye.site/2020/11/02/%E6%90%AD%E5%BB%BAfastdfs</link><description><![CDATA[<img src="https://blog.yingye.site/plugins/feed/assets/telemetry.gif?title=%5B%E8%AE%B0%E5%BD%95%5D%20Ubuntu18.04%20%E6%90%AD%E5%BB%BAFastDFS%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8%E7%B3%BB%E7%BB%9F&amp;url=/2020/11/02/%E6%90%AD%E5%BB%BAfastdfs" width="1" height="1" alt="" style="opacity:0;">
<h2 id="系统版本">系统版本</h2>
<ul>
 <li>两台虚拟机，系统都为Ubuntu 18.04.5 LTS</li>
 <li>虚拟机ip分别为
  <ul>
   <li>192.168.10.10 这台虚拟机用作tracker服务器</li>
   <li>192.168.10.11 这台虚拟机用作storage服务器,该虚拟机为 192.168.10.10 的完全复制,所以系统环境完全一致</li>
  </ul></li>
</ul>
<h2 id="安装编译需要的软件">安装编译需要的软件</h2>
<pre><code class="prism language-shell">sudo apt update &amp;&amp; sudo apt install wget curl gcc make libpcre3 libpcre3-dev zlib1g-dev
</code></pre>
<h2 id="编译安装fastcommon">编译&amp;安装fastcommon</h2>
<ul>
 <li>当前最新版本为V1.0.43</li>
 <li>下载源码</li>
</ul>
<pre><code class="prism language-shell">wget https://github.com/happyfish100/libfastcommon/archive/V1.0.43.tar.gz
</code></pre>
<ul>
 <li>解压源码</li>
</ul>
<pre><code class="prism language-shell">tar -zxf V1.0.43.tar.gz
</code></pre>
<ul>
 <li>进入目录，编译并安装</li>
</ul>
<pre><code class="prism language-shell">cd libfastcommon-1.0.43 &amp;&amp; ./make.sh &amp;&amp; sudo ./make.sh install &amp;&amp; cd ~/
</code></pre>
<h2 id="编译安装fastdfs">编译&amp;安装fastDFS</h2>
<ul>
 <li>当前最新版本为V6.06</li>
 <li>下载源码</li>
</ul>
<pre><code class="prism language-shell">wget https://github.com/happyfish100/fastdfs/archive/V6.06.tar.gz
</code></pre>
<ul>
 <li>解压源码</li>
</ul>
<pre><code class="prism language-shell">tar -zxf V6.06.tar.gz
</code></pre>
<ul>
 <li>进入目录，编译并安装</li>
</ul>
<pre><code class="prism language-shell">cd fastdfs-6.06 &amp;&amp; ./make.sh &amp;&amp; sudo ./make.sh install
</code></pre>
<ul>
 <li>安装后再从源码中复制配置文件到安装目录</li>
</ul>
<pre><code class="prism language-shell">sudo cp conf/* /etc/fdfs/ &amp;&amp; cd ~/
</code></pre>
<h2 id="tracker设置">tracker设置</h2>
<p><a href="https://blog.csdn.net/qq_27384769/article/details/80603483">FastDFS 配置文件详解</a></p>
<ul>
 <li>修改 /etc/fdfs/tracker.conf 文件</li>
</ul>
<blockquote>
 <p># tracker所属的组
  <br>
  store_group=group1</p>
 <p># 目录地址(根目录必须存在,子目录会自动创建)
  <br>
  base_path=/media</p>
 <p># tracker 端口
  <br>
  port=22122</p>
 <p># http访问端口
  <br>
  http.server_port=8099</p>
</blockquote>
<h2 id="storage设置">storage设置</h2>
<ul>
 <li>修改 /etc/fdfs/storage.conf 文件</li>
</ul>
<blockquote>
 <p># storage所属的组
  <br>
  group_name=group1</p>
 <p># stroage端口
  <br>
  port=23000
  <br>
  # 目录地址(根目录必须存在,子目录会自动创建)
  <br>
  base_path=/media</p>
 <p># 逐一配置store_path个路径，索引号基于0。注意配置方法后面有0,1,2 …，需要配置0到store_path - 1
  <br>
  # 如果不配置base_path0，那边它就和base_path对应的路径一样
  <br>
  store_path0=/media</p>
 <p># tracker服务器的ip以及端口,ip不能为127.0.0.1以及localhost
  <br>
  tracker_server=192.168.10.10:22122</p>
 <p># http访问端口
  <br>
  http.server_port=8099</p>
</blockquote>
<ul>
 <li>修改 /etc/fdfs/client.conf 文件</li>
</ul>
<blockquote>
 <p>base_path=/media</p>
 <p>tracker_server=192.168.10.10:22122</p>
 <p>http.tracker_server_port=8099</p>
</blockquote>
<h2 id="测试">测试</h2>
<ul>
 <li>开启tracker服务器</li>
</ul>
<pre><code class="prism language-shell">sudo fdfs_trackerd /etc/fdfs/tracker.conf start
</code></pre>
<ul>
 <li>开启storage服务器</li>
</ul>
<pre><code class="prism language-shell">sudo fdfs_storaged /etc/fdfs/storage.conf start
</code></pre>
<ul>
 <li>在storage服务器执行命令测试上传,anti-steal.jpg为在源码的conf文件夹下有,之前copy过</li>
</ul>
<pre><code class="prism language-shell">fdfs_test /etc/fdfs/client.conf upload /etc/fdfs/anti-steal.jpg
</code></pre>
<h2 id="编译安装nginx">编译安装nginx</h2>
<ul>
 <li>早在FastDFS V4.05版本的时候就删除了对于HTTP的支持,所以即使上传成功了,使用给出的url也无法访问到文件,所以在这里用到nginx</li>
 <li>编译nginx所需的依赖</li>
</ul>
<pre><code class="prism language-shell">sudo apt-get install libpcre3 libpcre3-dev zlib1g-dev
</code></pre>
<ul>
 <li>下载nginx源码，当前最新版本为1.19.4</li>
</ul>
<pre><code class="prism language-shell">wget https://github.com/nginx/nginx/archive/release-1.19.4.tar.gz
</code></pre>
<ul>
 <li>解压nginx源码并进入目录</li>
</ul>
<pre><code class="prism language-shell">tar -zxf release-1.19.4.tar.gz &amp;&amp; cd nginx-release-1.19.4
</code></pre>
<ul>
 <li>生成Makefile,prefix指向的目录是之后的安装目录</li>
</ul>
<pre><code class="prism language-shell">./auto/configure --prefix="/etc/nginx" \
        --with-http_gzip_static_module \
        --with-http_stub_status_module \
        --with-pcre \
        --with-http_realip_module \
        --with-http_flv_module \
        --with-http_mp4_module \
        --with-http_secure_link_module \
        --with-http_random_index_module \
        --with-http_v2_module
</code></pre>
<ul>
 <li>编译并安装nginx</li>
</ul>
<pre><code class="prism language-shell">make -j$[$(nproc)+1] 
</code></pre>
<blockquote>
 <p>或者使用查看实时的编译信息</p>
</blockquote>
<pre><code class="prism language-shell">make -j$[$(nproc)+1] V=s
</code></pre>
<p>安装</p>
<pre><code class="prism language-shell">sudo make install
</code></pre>
<h2 id="配置nginx">配置nginx</h2>
<blockquote>
 <p><a href="https://blog.csdn.net/prcyang/article/details/89946190">nginx配置参考来源</a></p>
</blockquote>
<ul>
 <li>配置文件的路径为安装目录下的 conf/nginx.conf 文件</li>
 <li>上面在生成 Makefile 时指定的为 /etc/nginx , 那么完整的路径则为 /etc/nginx/conf/nginx.conf</li>
 <li>在 nginx.conf 的http区域中加入如下内容</li>
</ul>
<pre><code class="prism language-plaintext">server {
    # 这里因为storage和tracker都是设置的8099,为了上传完文件后不用每次访问时都修改端口,这里选择与其保持一致
    listen       8099;
    server_name  localhost-fdfs;
 
    location /group1/M00{
        # 路径为 /etc/fdfs/storage.conf 中的 base_path 的路径加上 data
        alias /media/data/;
        autoindex on;
    }
 
    error_page   500 502 503 504  /50x.html;
        location = /50x.html {
        root   html;
    }
}
</code></pre>
<ul>
 <li>创建nginx的service文件，方便用于启动或停止nginx</li>
</ul>
<blockquote>
 <p>创建文件</p>
</blockquote>
<pre><code class="prism language-shell">sudo touch /etc/systemd/system/nginx.service
</code></pre>
<blockquote>
 <p>添加内容</p>
</blockquote>
<pre><code class="prism language-shell">[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/etc/nginx/logs/nginx.pid
ExecStartPre=/etc/nginx/sbin/nginx -t
ExecStart=/etc/nginx/sbin/nginx -c /etc/nginx/conf/nginx.conf
ExecReload=/etc/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT \$MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
</code></pre>
<blockquote>
 <p>如果更改了nginx的安装目录,需要注意将 ‘/etc/nginx’ 改为自己的nginx安装目录
  <br>
  然后执行</p>
</blockquote>
<pre><code class="prism language-shell">sudo systemctl daemon-reload
</code></pre>
<pre><code class="prism language-shell">sudo service nginx start
</code></pre>
<p>如果nginx启动成功，那么就可以使用之前测试时给出的url去访问文件了</p>]]></description><guid isPermaLink="false">/2020/11/02/%E6%90%AD%E5%BB%BAfastdfs</guid><dc:creator>Administrator</dc:creator><category>默认分类</category><category>Ubuntu</category><category>Linux</category><pubDate>Mon, 2 Nov 2020 01:36:24 GMT</pubDate></item></channel></rss>