[{"content":"前言 上文《使用cloudflare的R2创建免费图床》为了无人机拍摄的照片有个好去，利用cloudflareR2搭建图床。今天继续将博客文章中的资源（图片、附件、视频等）通过picgo批量迁移到图床上，以减轻git版本仓库体积，这里分享下我的实践。\n正文 博客文章结构如下，准备将文章同级目录下的图片、附件、视频按原结果上传到图床，然后再修改文章（index.md）内容中的引用方式，将![1.png](1.png)改成![1.png](https://image.vitshare.cn/blog/content/post/demo/1.png),最后清理本地附件，精简git仓库。\n├── content │ └── post │ ├── 分享windows平板屏幕电量监控程序，python全套源码开放 │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 源码.zip │ │ └── index.md │ ├── 《蛤蟆先生去看心理医生》读后感 │ │ ├── 1.jpeg │ │ └── index.md │ └── 获取《王者荣耀》全英雄高清无码图 │ ├── 20180607141717128.png │ ├── 20180607141729211.png │ ├── 20180607144357253.png │ └── index.md 修改picgo命令行部分代码 查阅picgo参数，发现命令行版本无法修改云端路径，所以我通过环境变量和setconfig方法曲线救国,具体修改如下：\n#!/usr/bin/env node const path = require(\u0026#39;path\u0026#39;) const minimist = require(\u0026#39;minimist\u0026#39;) const argv = minimist(process.argv.slice(2)) let configPath = argv.c || argv.config || \u0026#39;\u0026#39; if (configPath !== true \u0026amp;\u0026amp; configPath !== \u0026#39;\u0026#39;) { configPath = path.resolve(configPath) } else { configPath = \u0026#39;\u0026#39; } const { PicGo } = require(\u0026#39;..\u0026#39;) const picgo = new PicGo(configPath) picgo.registerCommands() /* 新增部分 */ let uploadPath = process.env.uploadPath picgo.log.info(uploadPath) if ( uploadPath !==\u0026#39;\u0026#39;){ const current = picgo.getConfig(\u0026#39;picBed.current\u0026#39;) picgo.setConfig({ \u0026#39;picBed.aws-s3.uploadPath\u0026#39; : uploadPath }) } /* 新增部分 end*/ try { picgo.cmd.program.parse(process.argv) } catch (e) { picgo.log.error(e) if (process.argv.includes(\u0026#39;--debug\u0026#39;)) { Promise.reject(e) } } 批量上传脚本 find . -iname \u0026#34;*.jpg\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.jpeg\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.png\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.webp\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.webm\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.rar\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.zip\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.mp4\u0026#34; \u0026gt;\u0026gt; 1.sh find . -iname \u0026#34;*.mov\u0026#34; \u0026gt;\u0026gt; 1.sh #组织picgo上传指令 vim 1.sh :%s/\\(^.*\\)$/uploadPath=\\1 picgo -c ~\\/.config\\/picgo\\/data.json upload \\1/g :%s/^uploadPath=\\/home\\/zfj\\/workspace\\//uploadPath=/g 最后1.sh中内容\nuploadPath=blog/content/post/我的家用NAS应用场景/4.png picgo -c ~/.config/picgo/data.json upload /home/zfj/workspaces/blog/content/post/我的家用NAS应用场景/3.png uploadPath=blog/content/post/我的家用NAS应用场景/5.png picgo -c ~/.config/picgo/data.json upload /home/zfj/workspaces/blog/content/post/我的家用NAS应用场景/4.png uploadPath=blog/content/post/记录下曾经的电子玩具/9.png picgo -c ~/.config/picgo/data.json upload /home/zfj/workspaces/blog/content/post/记录下曾经的电子玩具/9.png uploadPath=blog/content/post/记录下曾经的电子玩具/6.png picgo -c ~/.config/picgo/data.json upload /home/zfj/workspaces/blog/content/post/记录下曾经的电子玩具/6.png 执行脚本批量上传\nbash ./1.sh 批量修改文章引用 find . -iname \u0026#34;*.jpg\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.jpeg\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.png\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.webp\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.webm\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.rar\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.zip\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.mp4\u0026#34; \u0026gt;\u0026gt; 2.sh find . -iname \u0026#34;*.mov\u0026#34; \u0026gt;\u0026gt; 2.sh vim 2.sh :set ignorecase #忽略大小写 :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.jpg\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.jpeg\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.png\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.webp\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.webm\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.mp4\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.mov\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.rar\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g :%s/^\\/home\\/zfj\\/workspaces\\/blog\\/content\\/post\\/\\(.*\\)\\/\\(.*.zip\\)/sed -i \u0026#34;s\\/\\2\\/https:\\\\\\/\\\\\\/image.vitshare.cn\\\\\\/blog\\\\\\/content\\\\\\/post\\\\\\/\\1\\\\\\/\\2\\/g\u0026#34; \\/home\\/zfj\\/workspaces\\/blog\\/content \\/post\\/\\1\\/index.md /g 最后2.sh内容\nsed -i \u0026#34;s/3.jpg/https:\\/\\/image.vitshare.cn\\/blog\\/content\\/post\\/我的家用NAS应用场景\\/3.jpg/g\u0026#34; /home/zfj/workspaces/blog/content/post/我的家用NAS应用场景/index.md sed -i \u0026#34;s/4.jpg/https:\\/\\/image.vitshare.cn\\/blog\\/content\\/post\\/我的家用NAS应用场景\\/4.jpg/g\u0026#34; /home/zfj/workspaces/blog/content/post/我的家用NAS应用场景/index.md sed -i \u0026#34;s/9.jpg/https:\\/\\/image.vitshare.cn\\/blog\\/content\\/post\\/记录下曾经的电子玩具\\/9.jpg/g\u0026#34; /home/zfj/workspaces/blog/content/post/记录下曾经的电子玩具/index.md sed -i \u0026#34;s/6.jpg/https:\\/\\/image.vitshare.cn\\/blog\\/content\\/post\\/记录下曾经的电子玩具\\/6.jpg/g\u0026#34; /home/zfj/workspaces/blog/content/post/记录下曾经的电子玩具/index.md sed -i \u0026#34;s/11.jpg/https:\\/\\/image.vitshare.cn\\/blog\\/content\\/post\\/记录下曾经的电子玩具\\/11.jpg/g\u0026#34; /home/zfj/workspaces/blog/content/post/记录下曾经的电子玩具/index.md 总结 附件迁移很顺利，到时cloudfalre上跨域名访问折腾了半天。一直返回403（跨域访问），排除了半天最后发现是Hotlink Protection策略惹的祸。\n","date":"2026-05-23T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/%E5%8D%9A%E5%AE%A2%E7%85%A7%E7%89%87%E9%99%84%E4%BB%B6%E6%89%B9%E9%87%8F%E8%BF%81%E7%A7%BBcloudflareR2%E5%AD%98%E5%82%A8/title.png","permalink":"https://zfj1441.com/p/%E5%8D%9A%E5%AE%A2%E7%85%A7%E7%89%87%E9%99%84%E4%BB%B6%E6%89%B9%E9%87%8F%E8%BF%81%E7%A7%BBcloudflarer2%E5%AD%98%E5%82%A8/","title":"博客照片附件批量迁移cloudflareR2存储"},{"content":"前言 我的博客图片视频没什么要求，本来没什么用户流程，所以图片、视频、附件怎么方便怎么来，基本上都是直接和每篇内容放一起统一上传。但最近搞无人机航拍，视频、照片清晰度很高文件很大，安装以往统一和内容放在一起上传GIT仓库，导致整个GIT仓库体积极速上升。多台电脑之间同步非常困难，这让我突然意识到是时候做内容与资源分离了。图床选择就不多说网上太多，这里选择cloudflare R2 + Piclist(PicGo)方案。\n正文 前提条件：已注册cloudflare、已绑定域名\ncloudflare的R2对象存储配置 开通R2对象存储 「存储和数据库」-\u0026gt;「R2对象存储」-\u0026gt;「概述」-\u0026gt; 绑定信用卡（可找咸鱼）和地址信息（可国内地址）-\u0026gt;「将R2订阅添加到我的帐户」\nR2基础设置 绑定图床域名和CORS策略 「存储和数据库」-\u0026gt;「R2对象存储」-\u0026gt;R2仓库（如：test）-\u0026gt;「设置」\n安全配置（防盗图、防刷流量） 「域名」-\u0026gt;「概览」-\u0026gt;图床域名（如：vitshare.cn）-\u0026gt;「安全性」-\u0026gt;「安全规则」-\u0026gt;「添加规则」分别添加“速率限制规则” 和 “自定义规则”\n为客户端创建API令牌 「存储和数据库」-\u0026gt;「R2对象存储」-\u0026gt;「概述」-\u0026gt;「管理」创建“帐户 API 令牌” 或“用户 API 令牌” ，配置权限和仓库名称，最后保存“访问密钥ID”、“机密访问密钥”、“\u0026hellip;\u0026hellip;.终节点”\n客户端配置(PicList) 测试验证 总结 网上教程非常详细纯无脑操作，对我来说最大的难点反而是绑定信用卡。下一步看看怎么迁移把历史图片全迁移到图床上。\n参考 Cloudflare R2 白嫖指南：10G存储+免流量费，打造免费图床\n用 Cloudflare R2 + PicGo 搭建高速图床\n搭建免费高速图床 CLoudFlare R2 +PicGo方案\n","date":"2026-05-10T00:00:00Z","image":"https://image.vitshare.cn/2026/05/d1455f2018c354ef01211b42b6319106.webp","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8cloudflare%E7%9A%84r2%E5%88%9B%E5%BB%BA%E5%85%8D%E8%B4%B9%E5%9B%BE%E5%BA%8A/","title":"使用cloudflare的R2创建免费图床"},{"content":"前言 年初，PDD 偶然向我推荐了大疆 Mini 系列入门款，那一刻我突然意识到：原来无人机也是普通人轻松驾驭的“新玩具”。随后刷视频就经常看到各种无人机拍摄的俯视美景震撼，加之大疆近期推出了平价的新品，没忍住剁手lito x1基础款 + 1年期安心飞服务。\n正文 为了给自己的这次“入坑”行为找个合理的理由，我各种咨询AI助手，深入分析无人机在日常生活中的实际价值。最后结合自家的生活现状，我找到了两个极具说服力的应用场景：\n1. 提升全家出门意愿\n小朋友虽然越来越大，但似乎越来越宅，每次喊他出门找各种理由推脱。我想如果能给这个“新玩具”一点诱惑力呢？无人机拍摄的过程充满了互动性，孩子们往往觉得新鲜好玩。\n2. “上帝视角”全家福\n前几年旅游时，想要一张完美的全家福，要么求助路人大叔，要么牺牲家里某位“摄影师”。\n3. 换个视角记录生活\n随着二宝学会走路，又可以出远门玩耍。用无人机从不同视角记录一家人的生活。\nYour browser doesn't support HTML5 video. Here is a link to the video instead. Your browser doesn't support HTML5 video. Here is a link to the video instead. 总结 初入无人机，啥也不懂乱飞。\n","date":"2026-05-05T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/入手大疆无人机lito_x1/title.png","permalink":"https://zfj1441.com/p/%E5%85%A5%E6%89%8B%E5%A4%A7%E7%96%86%E6%97%A0%E4%BA%BA%E6%9C%BAlito_x1/","title":"入手大疆无人机lito_x1"},{"content":"前言 书接上文《给自己搭一套本地AI大模型》投入300元，本地搭建AI大模型，最终实现openclaw的token自由。这几周继续压榨Tesla M60的剩余价值，利用Comfyui在本地搭建AI文生图，这里记录分享整体安装过程。\n正文 网上教程很多，步骤都差不多：nvida驱动+cuda安装、python环境搭建、安装comfyui软件和必要模型。这次使用ubuntu24系统，尝试比较下这张显卡在不同系统中的效率。具体环境：ubuntu24系统，16G内存+16G缓存+8G显存\nnvida驱动+cuda安装 #1.检查系统中的NVIDIA显卡 lspci | grep -i nvidia #2.在“附加驱动”中切换nouveau驱动，然后重启 #3.重启后安装580驱动和12.6版本cuda wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb sudo dpkg -i cuda-keyring_1.1-1_all.deb sudo apt-get update ubuntu-drivers devices sudo apt install nvidia-driver-580 # 验证驱动安装 nvidia-smi sudo apt install cuda-toolkit-12-6 #显卡太老只能装12版本的cuda，否则comfyui会报错 # 验证cuda安装 nvcc --version #4.如何报错通过以下命令删除所有安装后重启，在重新驱动安装步骤 sudo apt --purge remove nvidia* sudo apt --purge remove \u0026#34;*cublas*\u0026#34; \u0026#34;cuda*\u0026#34; sudo apt autoremove 安装uv工具和python环境 curl -LsSf https://astral.sh/uv/install.sh | sh uv --version uv python list # 安装最新的 Python 3.12 uv python install 3.12 安装comfyui和必要模型 1. comfyui安装\n# 拉取comfyui源代码 git clone https://github.com/Comfy-Org/ComfyUI.git # 创建虚拟环境 cd ComfyUI \u0026amp;\u0026amp; uv venv --python=3.12 # 安装依赖插件 source .venv/bin/activate uv pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ## 替换torch组件版本 uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126 # 启动comfyui python3 main.py --listen 0.0.0.0 --disable-cuda-malloc 2. 下载必要模型\ncd models/vae wget https://www.modelscope.cn/models/Comfy-Org/z_image_turbo/resolve/master/split_files/vae/ae.safetensors cd models/diffusion_models wget https://hf-mirror.com/T5B/Z-Image-Turbo-FP8/resolve/main/z-image-turbo-fp8-e4m3fn.safetensors wget https://modelscope.cn/models/Comfy-Org/z_image_turbo/resolve/main/split_files/diffusion_models/z_image_turbo_bf16.safetensors cd models/text_encoders wget https://modelscope.cn/models/Comfy-Org/z_image_turbo/resolve/main/split_files/text_encoders/qwen_3_4b.safetensors 3. 测试验证效果\n# 重新启动comfyui source .venv/bin/activate python3 main.py --listen 0.0.0.0 --disable-cuda-malloc 提升词：\n《新世纪福音战士》明日香cos，鹅蛋脸，蓝眼睛，穿着校服，微笑，眨眼，低头但眼睛看向观众，手指指向观众。角色胸口挂着“三新东市实验中学”中文字样的铭牌 写实风格，特写，环境光，摄像机从上往下俯拍，教室背景\n总结 全程看似简单，实则一点也不难，就是在网上各种找教程、看报错信息、排查问题。\n参考 uv入门教程\n魔塔社区\nUbuntu 24.04配置GPU开发环境全攻略，一文掌握NVIDIA How to install ComfyUI manually in different systems\nZ-Image 10组美女提示词，可收藏！\n阿里通义 Z-Image-Turbo 本地 ComfyUI 部署教程\n阿里 z-image 仅 6b 参数 8 步高效出图，是如何实现低成本高效率的？\n2026 最新 ComfyUI 教程 - 本地部署 AI 生图模型 - Z-Image-Turbo\n","date":"2026-04-21T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/comfyui本地部署AI生图模型/title.png","permalink":"https://zfj1441.com/p/comfyui%E6%9C%AC%E5%9C%B0%E9%83%A8%E7%BD%B2ai%E7%94%9F%E5%9B%BE%E6%A8%A1%E5%9E%8B/","title":"comfyui本地部署AI生图模型"},{"content":"前言 前段时间为学习 3D 建模在本机安装了 Fusion 360。在使用过程中发现其默认数据目录位于 C 盘，并且随着使用时间越长，占用空间也越来越大。为了优化存储结构并避免 C 盘空间爆满，我计划将 Fusion 360 的相关数据目录迁移至 D 盘。随后我在网上找到一个不错的教程，这里我尝试通过 AI 编程，将教程中手工执行的步骤改造为一个批处理（.bat）脚本，实现一键式的数据迁移。\n正文 实现原理 本文采用的核心技术是使用 mklink 命令创建符号链接（Symbolic Link）。 简单来说，这个过程就是将 D 盘的物理存储位置“伪装”成 C 盘原有的目录路径。对于操作系统和 Fusion 360 软件本身而言，它们依然认为数据存放在 C 盘的默认路径，但实际上所有的数据读写操作都被透明地重定向到了 D 盘的实际存储位置，从而实现了数据的无缝空间转移。\n操作准备清单 ⚠️ 重要！请仔细阅读 ⚠️\n⚠️ 重要！请仔细阅读 ⚠️\n⚠️ 重要！请仔细阅读 ⚠️\n提前下载并安装Fusion360 以“管理员身份”运行后续的批处理脚本，否则将因权限不足导致符号链接创建失败 脚本会删除原fusion360文件夹，重要数据请提前做好保存 @echo off chcp 65001 \u0026gt;nul ::Fusion360路径迁移与链接脚本 :: 获取当前用户名 set \u0026#34;USERNAME=%USERNAME%\u0026#34; echo. echo [步骤 1/2] 正在准备迁移 Fusion 360 数据... echo. :: --- 交互询问替换 pause --- :AskStart set /p \u0026#34;CONFIRM=即将关闭可能占用文件的 Autodesk 进程，请保存好工作。是否继续执行? (Y/N) [默认 Y]: \u0026#34; if /i \u0026#34;%CONFIRM%\u0026#34;==\u0026#34;N\u0026#34; ( echo 已取消操作。 exit /b 1 ) :: 尝试关闭常见进程 taskkill /F /IM \u0026#34;AutodeskDesktopApp.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;AdAppMgr.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;AcWebBrowser.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;ADPClientService.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;IPMClient.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;AdskIdentityManager.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 taskkill /F /IM \u0026#34;streamer.exe\u0026#34; \u0026gt;nul 2\u0026gt;\u0026amp;1 echo 进程清理尝试完成。 :: 设置默认路径 set \u0026#34;DEFAULT_PATH=D:\\Fusion360\u0026#34; echo. set /p \u0026#34;TARGET_PATH=请输入目标路径 [默认: %DEFAULT_PATH%] ： \u0026#34; :: 如果用户直接回车，则使用默认路径 if \u0026#34;%TARGET_PATH%\u0026#34;==\u0026#34;\u0026#34; set \u0026#34;TARGET_PATH=%DEFAULT_PATH%\u0026#34; :: 检查路径是否存在，不存在则创建 if not exist \u0026#34;%TARGET_PATH%\u0026#34; ( echo 路径 \u0026#34;%TARGET_PATH%\u0026#34; 不存在，正在创建... mkdir \u0026#34;%TARGET_PATH%\u0026#34; ) :: 定义源路径和目标路径 set \u0026#34;LOCAL_SRC=C:\\Users\\%USERNAME%\\AppData\\Local\\Autodesk\u0026#34; set \u0026#34;ROAMING_SRC=C:\\Users\\%USERNAME%\\AppData\\Roaming\\Autodesk\u0026#34; set \u0026#34;F360_SRC=C:\\Users\\%USERNAME%\\AppData\\Roaming\\Fusion360\u0026#34; set \u0026#34;LOCAL_TGT=%TARGET_PATH%\\Local\\Autodesk\u0026#34; set \u0026#34;ROAMING_TGT=%TARGET_PATH%\\Roaming\\Autodesk\u0026#34; set \u0026#34;F360_TGT=%TARGET_PATH%\\Roaming\\Fusion360\u0026#34; echo. echo 目标路径已确认为: \u0026#34;%TARGET_PATH%\u0026#34; echo 正在使用 Robocopy 迁移文件... echo \u0026#34;%LOCAL_SRC% --\u0026gt; %LOCAL_TGT%\u0026#34; echo \u0026#34;%ROAMING_SRC% --\u0026gt; %ROAMING_TGT%\u0026#34; echo \u0026#34;%F360_SRC% --\u0026gt; %F360_TGT%\u0026#34; echo. :: --- 交互询问替换 pause --- :AskStart set /p \u0026#34;CONFIRM=是否继续执行? (Y/N) [默认 Y]: \u0026#34; if /i \u0026#34;%CONFIRM%\u0026#34;==\u0026#34;N\u0026#34; ( echo 已取消操作。 pause exit /b 1 ) :: 使用 robocopy 复制并保留所有权限 (/COPYALL) :: /E 复制子目录 /MOV 移动文件 (复制后删除源) /R:1 重试1次 /W:1 等待1秒 robocopy \u0026#34;%LOCAL_SRC%\u0026#34; \u0026#34;%LOCAL_TGT%\u0026#34; /E /COPYALL /R:1 /W:1 robocopy \u0026#34;%ROAMING_SRC%\u0026#34; \u0026#34;%ROAMING_TGT%\u0026#34; /E /COPYALL /R:1 /W:1 robocopy \u0026#34;%F360_SRC%\u0026#34; \u0026#34;%F360_TGT%\u0026#34; /E /COPYALL /R:1 /W:1 echo. echo 文件复制完成。正在清理原始文件夹... echo. :: 由于 robocopy /MOV 可能无法删除被占用的空文件夹，我们手动尝试删除 rmdir /s /q \u0026#34;%LOCAL_SRC%\u0026#34; 2\u0026gt;nul rmdir /s /q \u0026#34;%ROAMING_SRC%\u0026#34; 2\u0026gt;nul rmdir /s /q \u0026#34;%F360_SRC%\u0026#34; 2\u0026gt;nul echo. echo [步骤 2/2] 正在创建符号链接... echo. :: 创建符号链接 mklink /D \u0026#34;%LOCAL_SRC%\u0026#34; \u0026#34;%LOCAL_TGT%\u0026#34; mklink /D \u0026#34;%ROAMING_SRC%\u0026#34; \u0026#34;%ROAMING_TGT%\u0026#34; mklink /D \u0026#34;%F360_SRC%\u0026#34; \u0026#34;%F360_TGT%\u0026#34; echo. echo ----------------------------------------------------------------------- echo 全部完成！ echo ----------------------------------------------------------------------- pause 总结 以上脚本仅在本机win10测试验证有效，对于有需要朋友可以试试。\n参考 Fusion 360 安装路径更改脚本\n","date":"2026-04-10T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/更换fusion360默认安装位置/title.png","permalink":"https://zfj1441.com/p/%E6%9B%B4%E6%94%B9fusion360%E9%BB%98%E8%AE%A4%E5%AE%89%E8%A3%85%E4%BD%8D%E7%BD%AE/","title":"更改fusion360默认安装位置"},{"content":"前言 去年给家人换了遥遥领先牌手机，用群晖备份照片时发现的照片非动态照片。琢磨着备份动态照片不是常规功能吗？怎么到华为手机上就不灵了，随后展开了问题的探究。\n正文 https://www.zhihu.com/question/6796868974\nhttps://github.com/lutao043/MotionPhotoConverter\nhttps://github.com/Bai-Mirror/pyheic_struct\nhttps://blog.0to1.cf/posts/cn-motion-photo-format/\n总结 都2026年了，动态照片还不能统一，就和USB接口一样鱼龙混杂。\n","date":"2026-03-29T00:00:00Z","image":"https://picsum.photos/800/600?r=2026-03-29","permalink":"https://zfj1441.com/p/%E6%B7%B7%E4%B9%B1%E7%9A%84%E5%8A%A8%E6%80%81%E7%85%A7%E7%89%87/","title":"混乱的动态照片"},{"content":"前言 最近openclaw风生水起，各大厂商推出了专属的“龙虾服务”。云主机服务商提供“龙虾”专用免费云主机；互联网大厂发布自家“龙虾”客户端并免费送token。但免费只是引流，等客户使用习惯后收费项目就会逐步展开。比如云主机仅限免费30天，免费token额度越送越少。所以要随心所欲长期折腾AI，还得是本地自建一套大模型，一次性投入解决token焦虑。在此记录分享我最近从零开始本地搭建大模型过程及趟过的坑。\n正文 显卡选择 首次本地搭建大模型，所以目标比较低：\nopenclaw能有不错畅快聊天体验（尝试过纯CPU跑模型，5分钟回一个消息完全没有客户体验） 简单信息查询，资料整理 基于以上需求，我计划投入400RMB尝试下本地搭建大模型。在海鲜市场逛了一圈后看中了Nvidia的Tesla系列M60显卡16G显存，300元左右（但在安装ollama就有点后悔了）\n基础设置 主机电源500w以上 主板Bios中有Above 4G选项并开启。（我的微星Z170，bios里找了半天没找到，后面更新Bios固件后找到该选项） 设置主板Bios，默认使用集显或亮机卡进操作系统（这坑上我趟了3天，如果没设置正常开机会黑屏或花屏） tesla显卡专用8Pin电源线，与主机电源8Pin线不通用需单独购买转接线。 驱动及CUDA安装 基本都按网上教程一步步操作，需要注意驱动和CUDA版本不能随意装，有绑定关系。可以先安装驱动，然后通过命令nvidia-smi -L 查看驱动支持最高的CUDA版本，再去下载安装CUDA\n安装显卡驱动\n安装CUDA\n显卡调试\nnvidia-smi -l 2 ollama安装配置 安装ollama在方法教程里面看到了这个表格，就知道显卡性能，再参考咸鱼价格就更好的选择。\nCompute Capability Family Cards 9.0 NVIDIA H100 8.9 GeForce RTX 40xx RTX 4090, RTX 4080, RTX 4070 Ti, RTX 4060 Ti NVIDIA Professional L4, L40, RTX 6000 8.6 GeForce RTX 30xx RTX 3090 Ti, RTX 3090, RTX 3080 Ti, RTX 3080, RTX 3070 Ti, RTX 3070, RTX 3060 Ti, RTX 3060 NVIDIA Professional A40, RTX A6000, RTX A5000, RTX A4000, RTX A3000, RTX A2000 8.0 NVIDIA A10, A16, A2 7.5 GeForce GTX/RTX GTX 1650 Ti, TITAN RTX, RTX 2080 Ti, RTX 2080, RTX 2070, RTX 2060 NVIDIA Professional T4, RTX 5000, RTX 4000, RTX 3000, T2000, T1200, T1000, T600, T500 Quadro RTX 8000, RTX 6000, RTX 5000, RTX 4000 7.0 NVIDIA TITAN V, V100, Quadro GV100 6.1 NVIDIA TITAN TITAN Xp, TITAN X GeForce GTX GTX 1080 Ti, GTX 1080, GTX 1070 Ti, GTX 1070, GTX 1060, GTX 1050 Quadro P6000, P5200, P4200, P3200, P5000, P4000, P3000, P2200, P2000, P1000, P620, P600, P500, P520 NVIDIA Tesla P40, P4 6.0 NVIDIA Quadro GP100 5.2 GeForce GTX GTX TITAN X, GTX 980 Ti, GTX 980, GTX 970, GTX 960, GTX 950 Quadro M6000 24GB, M6000, M5000, M5500, M, M4000, M2200, M2000, M620 NVIDIA Tesla M60, M40 5.0 GeForce GTX GTX 750 Ti, GTX 750, NVS 810 Quadro K2200, K1200, K620, M1200, M520, M5000, M, M4000, M, M3000, M, M2000, M, M1000, M, K620, M600, M500, M ollama安装\nnvidia-smi -L # 展示可用显卡 #修改ollama默认启动配置 CUDA_VISIBLE_DEVICES=0,1 #代表让ollama能识别到第几张显卡 OLLAMA_SCHED_SPREAD=1 #这几张卡均衡使用 OLLAMA_KEEP_ALIVE=-1 #模型一直加载, 不自动卸载 OLLAMA_HOST=0.0.0.0 #监听地址 OLLAMA_PORT=11434 #监听端口 openclaw本地配置 openclaw安装\n总结 记录分享我从选择显卡、BIOS设置、驱动安装到ollama配置过程中的坑，有需要的可以参考。\n参考 Tesla V100 在 Windows 下安装配置\nNvidia Tesla P100在WIN10下目前（2026年1月28号）能启用WDDM和CUDA的最新版本\nollama部署deepseek, 多显卡负载均衡\n保姆级教程 Ollama 部署 DeepSeek-R1 本地模型\n","date":"2026-03-28T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/给自己搭一套本地AI大模型/title.jpg","permalink":"https://zfj1441.com/p/%E7%BB%99%E8%87%AA%E5%B7%B1%E6%90%AD%E4%B8%80%E5%A5%97%E6%9C%AC%E5%9C%B0ai%E5%A4%A7%E6%A8%A1%E5%9E%8B/","title":"给自己搭一套本地AI大模型"},{"content":"前言 近期，飞牛 OS 曝出的高危漏洞事件为所有 NAS 用户提了一个醒：家庭网络与数据安全不容忽视。就我个人的检查而言，暂时未发现系统遭受入侵的迹象（当然，安全领域没有绝对，潜在风险可能尚未显现）。目前，我的家庭网络环境中同时运行着黑群晖与飞牛 OS 双系统。借此机会，我将系统梳理一下自己在 NAS 网络安全防护方面的设置与思路，希望能为大家提供一份实用的参考。\n正文 为方便家人远程访问与备份，我选择了 IPv6 公网直连的方案，具体实现方法可参考我之前的文章：《通过 IPv6 服务内网 nas》。\n路由器安全配置 默认关闭 SSH 等管理服务，仅在必须维护时临时开启，降低被扫描攻击的风险。 启用 IPv6 防火墙，仅放行 NAS 管理所必需的特定端口，屏蔽其他所有流量。 群晖安全设置 参考了“我不是矿神”的详尽教程《群晖系统安全设置 禁止所有外国IP访问 添加网络威胁黑名单》，并结合自身情况进行了调整，核心措施如下：\n禁用默认账号，如：admin、gust，为所有个人账户启用 OTP 双重认证（家人账户可视使用习惯酌情简化） 关闭非必需的 SSH 服务，并修改其默认端口 更改群晖 DSM 的默认 HTTP/HTTPS 访问端口（5000/5001）。 启用防火墙设置，允许局域网IP、docker容器IP、国内IP访问，禁止国外IP访问任何端口（重点）\n主动将已知的恶意 IP 地址段加入黑名单，进行前置封堵。 飞牛OS安全配置 鉴于近期的安全事件，飞牛OS 的防护同样重要，其配置思路与群晖基本一致，形成统一的安全基线：\n为所有个人账户启用 OTP 双重认证\n关闭非必需的 SSH 服务，并修改其默认端口\n修改默认 Web 管理端口（建议使用10000以上高位端口） 启用并配置防火墙，允许局域网IP、中国IP访问，其他IP拒绝 最后的兜底方案 没有绝对安全的系统，只有能否找回的数据。为了应对极端情况（如 0-day 漏洞利用或硬件故障），我还采用了 双机冷备 方案，备机每天凌晨同步主NAS数据。\n总结 以上是我个人NAS使用过程中的一些安全措施，上仅供大家参考。\n","date":"2026-02-04T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/NAS安全防护分享/index.png","permalink":"https://zfj1441.com/p/nas%E5%AE%89%E5%85%A8%E9%98%B2%E6%8A%A4%E5%88%86%E4%BA%AB/","title":"NAS安全防护分享"},{"content":"前言 去年7月份入手第一台3D打印机后，我就陷入了耗材焦虑。看到有耗材活动就情不自禁买买买，导致仓库里的耗材堆积成山。于是，我决定做一个耗材管理系统，对耗材进行登记，做到心中有数，不能再盲目剁手了。同时，最近看到很多vibe code的成功案例，我也手痒想体验一下这种开发方式的魅力。\n正文 成功案例 《记普通人的第一次vibe coding》，作者首次尝试vibe coding开发MonitorTimer（Windows显示器定时关闭工具），用于节能和保护屏幕隐私。\n《一个纯粹的在线拼图工具，完全免费不用登录》，后端工程师因写作时找不到顺手的在线拼图工具，突发奇想利用AI辅助开发了一个纯前端、免费免登录的拼图工具。\n《一个像素 ACT 游戏 demo》，作者耗时两个月使用Gemini3完成代码、美术和策划，开发出像素风格ACT游戏。\n我的实践 尝试过几款免费AI工具后，我发现Gemini3比较适合我这种AI水平不太高的人。\n1. 提出我的大概设想\n#提示词： 使用golang语言开发一套3d打印耗材出入库管理系统，具体功能：耗材出库入库，品牌管理。数据库使用sqlite3，网页端操作，入库登记时需要输入品牌、耗材类型、颜色、购买时间、重量、是否含料盘 2. 更换后端服务框架并打包网页文件\n因为之前项目中有用过mux库，所以我让后端服务AI改用mux库，同时为方便后期部署，要求AI程序能编译成单执行文件。\n#提示词： 第一请将后端服务框架换成mux库，第二为方面后续部署将index.html打包进执行程序中 3. 修复Bug并完善功能\n测试过程中，我发现了几个Bug并提醒AI进行修改。同时，经过几轮沟通，项目结构和需求也越来越清晰，我不断给AI提出更多的需求。\n#提示词： 在以上基础上修改： 第一品牌管理功能，增加可以删除品牌的功能，同时删除前检查是否仓库中有该品牌的耗材，如果有则禁止删除该品牌 第二耗材入库时，颜色改用可以自定义输入的选择框，可以选择白色、黑色、蓝色、红色、黄色、绿色，默认白色 第三增加统计功能，可按品牌统计、按耗材颜色统计 或 按耗材类型统计 4. 最后的成果\n大概1个小时与AI来回沟通，一个超简单的3D打印耗材出入库管理系统就诞生了。开发过程中，我只负责提需求，没有改过一行代码，AI的开发效率和能力真的让人佩服。\n总结 经过这次全流程vibe code体验，我真切地感受到软件开发的范式正在被彻底重构。过去，创意的落地需要跨越技术的高墙——你不仅要知道\u0026quot;做什么\u0026quot;，更要精通\u0026quot;怎么做\u0026quot;。从编程语言的语法细节，到数据库的选型设计，再到前后端的协同调试，每一步都可能成为创意的绊脚石。但现在，AI抹平了这些技术鸿沟，让开发者回归到最纯粹的价值创造环节。\n","date":"2026-01-24T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/我的vibecode之旅/title.png","permalink":"https://zfj1441.com/p/%E6%88%91%E7%9A%84vibe-code%E4%B9%8B%E6%97%85%E7%94%A8ai%E6%90%AD%E5%BB%BA3d%E6%89%93%E5%8D%B0%E8%80%97%E6%9D%90%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/","title":"我的vibe code之旅：用AI搭建3D打印耗材管理系统"},{"content":"前言 2025年的年终总结在火车上写。回顾2025的时间线，平淡的生活中有带着些不平凡。\n正文 2025年最大的变化：从三口之家变成四口之家。\n2025年最大的感触：哥重病住院，爸妈电话与我商量处理方案，让我突然觉得我开始要成为爸妈的依靠。\n2025年博客/公众号没找到新思路和方法，但尽可能每月一篇内容，保持月活。2025年公众号新增关注482人，文章最高1.9w访问量，还意外收获100rmb广告收入。\n2025年阅读方面，精读《上瘾》、《平凡的世界》、《蛤蟆先生去看心理医生》，通过安静阅读让浮躁的内心回归自我思考；同时也有助于治疗失眠（哈哈）。\n总结 2025年的计划任务，除了体重其他都完成的还过得去，那么2026年计划：\n减肥锻炼身体，早睡 精读4本书并摘选重要内容 博客，通过AI构建网站主页、足迹、年度计划执行 solidworks建模学习，makerworld发布4款自己设计作品 仓库布局整理，将迁打印机迁至仓库，完善排风等智能化改造 ","date":"2026-01-19T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/2025年终总结/1.png","permalink":"https://zfj1441.com/p/2025%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/","title":"2025年终总结"},{"content":"前言 最近飞牛NAS系统非常火，作为免费且支持ARM架构的系统，它甚至带动了闲鱼上各种旧电视盒子的涨价。上周末，我用一台2013年的旧笔记本安装并体验了一番，下面从一名黑群晖用户的角度，分享一些实际感受。\n正文 系统安装 这一步对于玩NAS的朋友来说应该不陌生，操作比较简单，我简要描述一下我的安装过程：\n硬件准备：8GB以上U盘 软件准备：Ventoy、飞牛NAS系统镜像（fnos） 制作启动U盘：解压Ventoy，运行ventoy2disk.exe，将U盘制作成启动盘 将飞牛系统镜像拷贝到U盘中 将电脑启动项改为U盘启动，进入Ventoy界面后选择飞牛镜像 按照提示默认安装即可 安装完成后，在另一台电脑的浏览器中输入飞牛NAS的IP地址，即可进入系统 使用体验 我的NAS使用场景主要包括：照片备份、远程下载、远程观影、摄像头接入、Docker及虚拟机。下面结合这些场景谈谈飞牛NAS的表现。\n远程访问\n对于黑群晖用户来说，远程访问往往需要折腾路由器设置、与运营商沟通公网IP，甚至搭建内网穿透。而飞牛NAS在这方面对新手非常友好——只需注册飞牛账号并登录绑定，远程访问功能一键开启，省心省力。\n照片备份\n飞牛的相册管理功能借鉴了群晖的成熟设计，从照片上传、展示到存储结构都似曾相识，黑群晖用户可以无缝切换。此外，飞牛在AI功能上投入不少，支持人脸识别、视频人脸识别、重复文件检测、以文搜图等，功能相当全面。\n远程下载\n对于轻度下载用户，系统自带的下载工具足够应对每周的剧集更新或偶尔的电影下载。群晖自带的下载工具有时需要手动添加Tracker才能提升下载效率，而飞牛则贴心地支持预配置Tracker列表，添加任务时会自动生效，下载体验更顺畅。\ndocker、虚拟机、三方软件\nDocker和虚拟机这类核心功能飞牛都已具备，满足日常扩展需求。第三方应用方面，我虽无特殊要求，但看到主流的Alist、云盘同步等工具都已上架，生态正在快速完善。\n不足之处\n相比群晖，飞牛目前缺少官方摄像头监控套件和音乐服务。此外，相册的AI人脸识别精度还有提升空间，偶尔会出现令人啼笑皆非的分类（比如把8岁小朋友和60岁老爷爷归为同一人）。\n总结 对于新手来说，用一台旧笔记本就能轻松体验NAS系统，飞牛NAS的入门门槛非常友好。后期若有更高需求，再升级专业设备也不迟。\n至于黑群晖用户是否要立即迁移到飞牛，目前我选择双系统并行，逐步将远程下载、Docker、虚拟机等功能迁移至飞牛NAS。而核心的照片与文件同步，我打算再等等看，同时也提前规划如何迁移现有的4TB数据。\n","date":"2025-12-22T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/用旧笔记本搭建飞牛NAS：黑群晖用户体验分享/1.png","permalink":"https://zfj1441.com/p/%E7%94%A8%E6%97%A7%E7%AC%94%E8%AE%B0%E6%9C%AC%E6%90%AD%E5%BB%BA%E9%A3%9E%E7%89%9Bnas%E9%BB%91%E7%BE%A4%E6%99%96%E7%94%A8%E6%88%B7%E4%BD%93%E9%AA%8C%E5%88%86%E4%BA%AB/","title":"用旧笔记本搭建飞牛NAS，黑群晖用户体验分享"},{"content":"前言 紧跟AI大语言模型潮流，给我的文章摘要添加\u0026quot;AI模式\u0026quot;实时输出。\n正文 主要参考Hi，AI 摘要 ，只不过我不喜欢把摘要单独放在data/目录下，所以简单修改了下 aisummary.html，让它直接显示Front Matter中summary的内容。\n完整配置如下：\n\u0026lt;div class=\u0026#34;post-ai\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;ai-title\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;ai-title-text\u0026#34;\u0026gt;AI 摘要\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- Typeit 打字机效果，不需要则注释掉下面这行代码 --\u0026gt; \u0026lt;div id=\u0026#34;ai-explanation\u0026#34; class=\u0026#34;ai-explanation\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;ai-explanation ai-explanation-content\u0026#34;\u0026gt; {{ if .Summary }} {{ .Summary }} {{ else }} AI 摘要接口暂时失联…… {{ end }} \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;!-- 打字机效果的 JS 不需要则注释掉 --\u0026gt; \u0026lt;script src=\u0026#34;https://npm.elemecdn.com/typeit@8.7.1/dist/index.umd.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script\u0026gt; document.addEventListener(\u0026#34;DOMContentLoaded\u0026#34;, function () { // 从 .ai-explanation-content 取值 const matchingSummary = document.querySelector(\u0026#34;.ai-explanation-content\u0026#34;).textContent; new TypeIt(\u0026#34;#ai-explanation\u0026#34;, { strings: matchingSummary, speed: 6, lifeLike: true, waitUntilVisible: true, }).go(); }); \u0026lt;/script\u0026gt; 总结 下次得找时间好好整理下文章摘要。\n参考 博客 AI 摘要及优化\nHi，AI 摘要\n","date":"2025-12-02T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-12-02","permalink":"https://zfj1441.com/p/%E7%BB%99hugo%E6%96%87%E7%AB%A0%E5%A2%9E%E5%8A%A0ai%E6%91%98%E8%A6%81/","title":"给hugo文章增加AI摘要"},{"content":"前言 最近在统信UOS系统上开发一个小工具，编译完成后却遭遇了“此应用没有通过系统验证，无法运行”的提示。经过排查，这正是UOS严格的安全机制在发挥作用——它要求用户必须激活并开启开发者模式，才能运行未经官方应用商店签名的程序。\n然而，我身处严格的内网环境，在线激活开发者模式几乎是一项不可能完成的任务。在多方尝试后，我摸索出了一条免开发者模式的“外门邪道”。\n正文 参考uos获取root权限的简单教程获取UOS的root权限 参考统信UOS命令行设置未签名软件安装权限_uos 安全中心 允许任意应用强行就开安全模式。但是我这UOS版本好像没这些配置，正当我郁闷时发现whitelist文件中的路径是不进行安全校验的，自己编译的程序可以正常执行。所以就后把执行程序的绝对路径写进whitelist就搞定了。 总结 以上纯个人折腾，仅供参考。\n参考 uos获取root权限的简单教程\n解决UOS或deepin提示“没有通过系统验证，无法运行”\n统信UOS命令行设置未签名软件安装权限_uos 安全中心 允许任意应用\n统信UOSv20专业版(1050)桌面操作系统设置root密码\n统信UOS 20 -1070获取root权限教程\nUOS 获取root权限\n","date":"2025-11-21T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-11-21","permalink":"https://zfj1441.com/p/uos%E5%85%8D%E5%BC%80%E5%8F%91%E8%80%85%E6%A8%A1%E5%BC%8F%E8%BF%90%E8%A1%8C%E8%87%AA%E5%88%B6%E7%A8%8B%E5%BA%8F/","title":"Uos免开发者模式运行自制程序"},{"content":"前言 这本书在2023年就看过一部分，后因种种原因中途放弃，最近“全年阅读计划”重拾阅读。\n正文 小说时间跨度从1975年到1985年，正好覆盖了 “文化大革命”后期到改革开放初期 这一中国社会发生巨变的十年。故事围绕黄土高原双水村孙玉厚一家展开：\n哥哥孙少安：\n一个13岁就辍学扛起家庭重担的农村青年。他立足于土地，敢于挑战旧规，率先实行生产责任制，后开办砖厂，历经无数次失败与成功。他的奋斗是为了让家人和乡亲们“吃饱穿暖”，过上体面的生活。 他与田润叶的爱情因城乡和身份的鸿沟而成为悲剧，最终与善良能干的贺秀莲结为夫妻。 弟弟孙少平：\n一个有文化、渴望走向更广阔世界的农村青年。他受过高中教育，对外面的世界充满向往。他做过揽工汉、煤矿工人，在极其艰苦的体力劳动中，始终依靠书籍保持精神的独立与高贵。 他与田晓霞（地委书记的女儿）的爱情，是超越阶级的精神共鸣，但最终以晓霞的牺牲而告终，成为全书最大的遗憾之一。 总结 第一次放弃阅读原因之一是少安与润叶的爱情的悲剧，为他们感到遗憾同时内心一时难以难以接受。 从少安少平一家故事，转头看看自己的生活，和他们比我们的生活是幸福的，不用为吃穿而苦恼（这大概也是长辈眼中常常感叹我们生的好时代）。但我们好像也是烦恼的，房贷、车贷、育儿、教育是我们这个时代的“责任制”和“烂包光景”。我们和少安少平一样，在属于自己的平凡世界里，扛起责任，迎接一个又一个的挑战。\n","date":"2025-11-14T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-11-14","permalink":"https://zfj1441.com/p/%E5%B9%B3%E5%87%A1%E7%9A%84%E4%B8%96%E7%95%8C%E8%AF%BB%E5%90%8E%E6%84%9F/","title":"《平凡的世界》读后感"},{"content":"前言 事情始于我计划安装SolidWorks时，系统无情地提示我磁盘空间不足。恰逢硬盘价格处于高位，暂时无法通过扩容解决问题。于是，一次针对硬盘空间的“精准清理”行动，迫在眉睫。\n正文 系统自带磁盘清理 在C盘或D盘右键属性-\u0026gt;磁盘清理-\u0026gt;清理系统文件,勾选系统更新、临时文件等,最后确认清除。\n卸载未使用的软件 开始菜单-\u0026gt;设置-\u0026gt;应用，卸载长期未使用的‘僵尸’软件。\n清理垃圾文件 这里使用一款名为 SpaceSniffer 的软件。deepseek介绍spacesniffer如下：\nSpaceSniffer是一款专为Windows系统设计的免费、免安装的磁盘空间分析工具。它通过直观的图形化界面，帮助你快速找出是哪些文件和文件夹占用了大量的硬盘空间。\n定位大文件\n通过SpaceSniffer扫描后，为我们展示“磁盘空间地图”，通过地图我们可以很清晰的知道空间都被那些文件或文件夹占用。我的磁盘占用主要有三块：\npagefile.sys文件独自占据了约16GB的宝贵空间。 hiberfil.sys文件 Download文件夹，长期遗留下的下载文件未能及时清理。 展开清理动作\npagefile.sys虚拟内存文件，可以从C盘迁移至空间相对宽裕的D盘，并对其大小进行了合理化设置，一举为C盘释放了16GB的巨量空间。 我的电脑右键-\u0026gt;属性-\u0026gt;高级系统设置-\u0026gt;高级-\u0026gt;设置(性能)-\u0026gt;高级-\u0026gt;更改(虚拟内存)。 hiberfil.sys是系统休眠使用的缓存文件，windows下我没有待机的习惯，通过cmd命令关闭休眠powercfg -h off\n残留的下载文件 有用的转移的NAS(网络存储)上,没用的就地删除。\n总结 通过以上方案有效给我腾出了空间,让我顺利安装上soliwork工具。以上我的磁盘清理方案,记录下已被下次需要。\n参考 缓存文件处理方法（pagefile.sys）\nc盘要炸了，怎么搞\nredtrillix/SpaceSniffer\n","date":"2025-11-07T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/windows硬盘空间清理/title.png","permalink":"https://zfj1441.com/p/windows%E7%A1%AC%E7%9B%98%E7%A9%BA%E9%97%B4%E6%B8%85%E7%90%86/","title":"windows硬盘空间清理"},{"content":"前言 从入手3D打印机后，模型就是跑不了的话题。从打印别人分享的模型开始，到逐步自建建模，今天终于分享发布我第一个3D模型《中式氛围灯内部ws2812灯带支撑》。\n正文 从8月份开始跟着视频学习funsion360建模，一个多月过去一些简单的模型已经基本能上手。期间也做了简单的模型，今天终于在makerworld上部分个人第一个模型。\n总结 学习建模过程中时看视频觉得比较简单：点、线、面、草图、拉升、抽壳等。但真上手自己画发现还是非常有难度，还是得时不时搜多解决问题。着实验证了那句老话：一看就会，一做就废。\n","date":"2025-10-12T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-10-12","permalink":"https://zfj1441.com/p/%E5%9C%A8makerworld%E4%B8%8A%E5%8F%91%E5%B8%83%E6%88%91%E4%B8%AA%E4%BA%BA%E7%AC%AC%E4%B8%80%E4%B8%AA3d%E6%A8%A1%E5%9E%8B%E4%BD%9C%E5%93%81/","title":"在makerworld上发布我个人第一个3D模型作品"},{"content":"前言 使用hugo搭建博客有段时间了，但是有个问题：hugo + stack主题建站很多资源，包括字体、js等依赖于国外服务，加载起来非常慢，导致“水土不服”。最近看到有大佬做了优化教程，所以跟着教程给我的网站做一次优化。\n正文 资源本地化(网站加速) custom-font字体文件\n下载custom-font.css样式文件，放到assets/css/custom-font.css 下载custom-font.css中使用的woff2字体文件，总共有6个放到static/fonts文件夹中 修改custom-font.css文件，将https://fonts.gstatic.com/s/lato/v25/xxxx.woff2替换为/fonts/xxxx.woff2 创建并修改layouts/partials/footer/components/custom-font.html {{ $customFont := resources.Get \u0026#34;css/custom-font.css\u0026#34; | minify | fingerprint }} \u0026lt;script\u0026gt; (function () { const customFont = document.createElement(\u0026#39;link\u0026#39;); customFont.href = \u0026#34;{{ $customFont.RelPermalink }}\u0026#34;; customFont.type = \u0026#34;text/css\u0026#34;; customFont.rel = \u0026#34;stylesheet\u0026#34;; document.head.appendChild(customFont); }()); \u0026lt;/script\u0026gt; vibrant.min.js等脚本文件\n下载vibrant.min.js文件，放到static/js/vibrant.min.js 创建并修改data/external.yaml Vibrant: - src: /js/vibrant.min.js # integrity: sha256-awcR2jno4kI5X0zL8ex0vi2z+KMkF24hUW8WePSA9HM= type: script PhotoSwipe: - src: https://unpkg.com/photoswipe@4.1.3/dist/photoswipe.min.js integrity: sha256-ePwmChbbvXbsO02lbM3HoHbSHTHFAeChekF1xKJdleo= type: script defer: true - src: https://unpkg.com/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js integrity: sha256-UKkzOn/w1mBxRmLLGrSeyB4e1xbrp4xylgAWb3M42pU= type: script defer: true - src: https://unpkg.com/photoswipe@4.1.3/dist/default-skin/default-skin.css type: style - src: https://unpkg.com/photoswipe@4.1.3/dist/photoswipe.css type: style KaTeX: - src: https://unpkg.com/katex@0.16.9/dist/katex.min.css integrity: sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV type: style - src: https://unpkg.com/katex@0.16.9/dist/katex.min.js integrity: sha384-XjKyOOlGwcjNTAIQHIpgOno0Hl1YQqzUOEleOLALmuqehneUG+vnGctmUb0ZY0l8 type: script defer: true - src: https://unpkg.com/katex@0.16.9/dist/contrib/auto-render.min.js integrity: sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05 type: script defer: true Cactus: - src: https://latest.cactus.chat/cactus.js integrity: type: script - src: https://latest.cactus.chat/style.css integrity: type: style 菜单栏增加项目 从tabler.io网站挑选合适的svg图片，放到assets/icons/中，如：assets/icons/soft.svg 在page/目录下创建目录文件夹及index.md，如：page/soft/index.md 和 page/soft/index.zh-cn.md 修改index.md 和 index.zh-cn.md --- title: 软件 subtitle: \u0026#34;个人常用windows软件清单\u0026#34; date: 2024-06-02 lastmod: 2024-06-02 author: \u0026#34;\u0026#34; menu: main: weight: -55 params: icon: soft #引用soft.svg图标 comments: false --- 升级waline评论 waline从v2.15.8升级到v3.6.0后，发现回复评论会@两次原作者。排查后发现问题：v2.15.8版本把@原作者存放在评论内容中然后直接展示评论内容，而v3.6.0版本@原作者是有逻辑处理的，所以导致升级v3.0.6后会@两次的现象。我不确定是不是升级后端服务能解决，反正我网站也没几条评论所以直接deepseek写个update语句搞定，最后的升级方案：\n在layouts/partials/comments/provider/waline.html保存失迹的博客大佬修改版本waline.html \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://unpkg.com/@waline/client@v3/dist/waline.css\u0026#34;/\u0026gt; \u0026lt;div id=\u0026#34;waline\u0026#34; class=\u0026#34;waline-container\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .waline-container { background-color: var(--card-background); border-radius: var(--card-border-radius); box-shadow: var(--shadow-l1); padding: var(--card-padding); --waline-font-size: var(--article-font-size); } .waline-container .wl-count { color: var(--card-text-color-main); } \u0026lt;/style\u0026gt; {{- $showReaction := (default true .Params.reaction) -}} {{- with .Site.Params.comments.waline -}} {{- $config := dict \u0026#34;el\u0026#34; \u0026#34;#waline\u0026#34; \u0026#34;dark\u0026#34; `html[data-scheme=\u0026#34;dark\u0026#34;]` -}} {{- $replaceKeys := dict \u0026#34;serverurl\u0026#34; \u0026#34;serverURL\u0026#34; \u0026#34;requiredmeta\u0026#34; \u0026#34;requiredMeta\u0026#34; \u0026#34;wordlimit\u0026#34; \u0026#34;wordLimit\u0026#34; \u0026#34;pagesize\u0026#34; \u0026#34;pageSize\u0026#34; \u0026#34;imageuploader\u0026#34; \u0026#34;imageUploader\u0026#34; \u0026#34;texrenderer\u0026#34; \u0026#34;texRenderer\u0026#34; \u0026#34;commentsorting\u0026#34; \u0026#34;commentSorting\u0026#34; \u0026#34;recaptchav3key\u0026#34; \u0026#34;recaptchaV3Key\u0026#34; \u0026#34;turnstilekey\u0026#34; \u0026#34;turnstileKey\u0026#34; -}} {{- $replaceLocaleKeys := dict \u0026#34;reactiontitle\u0026#34; \u0026#34;reactionTitle\u0026#34; \u0026#34;gifsearchplaceholder\u0026#34; \u0026#34;gifSearchPlaceholder\u0026#34; \u0026#34;nickerror\u0026#34; \u0026#34;nickError\u0026#34; \u0026#34;mailerror\u0026#34; \u0026#34;mailError\u0026#34; \u0026#34;wordhint\u0026#34; \u0026#34;wordHint\u0026#34; \u0026#34;cancellike\u0026#34; \u0026#34;cancelLike\u0026#34; \u0026#34;cancelreply\u0026#34; \u0026#34;cancelReply\u0026#34; \u0026#34;uploadimage\u0026#34; \u0026#34;uploadImage\u0026#34; -}} {{- range $key, $val := . -}} {{- if ne $val nil -}} {{- $replaceKey := index $replaceKeys $key -}} {{- $k := default $key $replaceKey -}} {{- if eq $k \u0026#34;locale\u0026#34; -}} {{- $locale := dict -}} {{- range $lkey, $lval := $val -}} {{- if ne $lval nil -}} {{- $replaceLKey := index $replaceLocaleKeys $lkey -}} {{- $lk := default $lkey $replaceLKey -}} {{- $locale = merge $locale (dict $lk $lval) -}} {{- end -}} {{- end -}} {{- $config = merge $config (dict $k $locale) -}} {{- else if eq $k \u0026#34;reaction\u0026#34; -}} {{- $config = merge $config (dict $k (cond $showReaction $val false)) -}} {{- else -}} {{- $config = merge $config (dict $k $val) -}} {{- end -}} {{- end -}} {{- end -}} \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import { init } from \u0026#39;https://unpkg.com/@waline/client@v3/dist/waline.js\u0026#39;; init({{ $config | jsonify | safeJS }}); \u0026lt;/script\u0026gt; {{- end -}} sqlite3数据库迁移 -- 检查是否有\u0026#34;[@匿名](#19):xxxx\u0026#34;数据 SELECT comment, CASE WHEN comment LIKE \u0026#39;[%]:%\u0026#39; THEN SUBSTR(comment, INSTR(comment, \u0026#39;]:\u0026#39;) + 2) WHEN comment LIKE \u0026#39;[%:%\u0026#39; THEN SUBSTR(comment, INSTR(comment, \u0026#39;:\u0026#39;) + 1) ELSE comment END AS new_comment FROM wl_comment WHERE comment LIKE \u0026#39;[%:%\u0026#39;; -- 更新数据 UPDATE wl_comment SET comment = CASE WHEN comment LIKE \u0026#39;[%]:%\u0026#39; THEN SUBSTR(comment, INSTR(comment, \u0026#39;]:\u0026#39;) + 2) WHEN comment LIKE \u0026#39;[%:%\u0026#39; THEN SUBSTR(comment, INSTR(comment, \u0026#39;:\u0026#39;) + 1) ELSE comment END WHERE comment LIKE \u0026#39;[%:%\u0026#39;; 文章末尾添加赞赏 给hugo博客添加赞赏支持功能，支持微信和支付宝作者写的非常详细，按教程无脑操作。\n总结 最近发现使用hugo建站的人越来越多，文档教程也越来越丰富。\n参考 使用hugo-stack搭建博客\n使博客更好地接入 Waline\n给hugo博客添加赞赏支持功能，支持微信和支付宝\n","date":"2025-10-07T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-10-07","permalink":"https://zfj1441.com/p/hugo%E5%8D%9A%E5%AE%A2%E7%BD%91%E7%AB%99%E4%BC%98%E5%8C%96/","title":"hugo博客网站优化"},{"content":"前言 今天整理资料发现，去年十一假期时为改光猫改桥接用路由器拨号（破解完后发现家里人不知道宽带账号和密码，得了白忙活一场），所使用的工具软件在这记录下。\n正文 https://image.vitshare.cn/blog/content/post/中兴光猫ZXHN-F650不拆机获取超级密码/附件1.zip\n总结 电信光猫破解需要严格对型号和版本，以上软件不保证100%成功。\n参考 中兴光猫ZXHN F650 获取光猫密码教程\n不拆机破解电信光猫的超级密码（适用于中兴ZXHN F650）\n中兴F650光猫 临时开启telnet获取超级密码 xxxx\n","date":"2025-09-26T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-09-26","permalink":"https://zfj1441.com/p/%E4%B8%AD%E5%85%B4%E5%85%89%E7%8C%ABzxhn-f650%E4%B8%8D%E6%8B%86%E6%9C%BA%E8%8E%B7%E5%8F%96%E8%B6%85%E7%BA%A7%E5%AF%86%E7%A0%81/","title":"中兴光猫ZXHN-F650不拆机获取超级密码"},{"content":"前言 关于3D打印机封箱主要有三点需求：第一是满足防尘需求。因我把打印机放在阳台，开放空间灰尘比较多； 第二极致的降噪需求。虽然拓竹通过主动降噪技术已经做的很安静，但是打印机经常会在夜间持续工作；第三通风换气。虽然PLA、PETG是环保安全的，但实际打印过程中仍然能闻到些异味（想想也是PLA、PETG材料是环保，但是添加的颜色辅料可不一定）。综上，封箱还是很有必要\n正文 封箱方案 来回看了很多种方案，有用2020铝型材、角钢、方木条、成品帆布、拓竹原装纸箱以及3D打印框架。以上方案不是结构简单，就是成本高投入大性价比低，无法满足需求。犹豫徘徊几周后发现用猫屋封箱，结构扎实且价格合适。\n空间改造 原计划是选用60x60x90，奈何咸鱼一直没有货，最后选择60x60x120。到手后去掉猫屋中所有配件包括原有层板（拆不下来最后上锯条），重新规划布局，然后在4个角加上铰链加固，最后效果。\n智能化改造 通过esp32 + 继电器，实现封箱内部照明、换气系统。下一步计划与homeassistant中温湿度计实现联动。\n成本核算 材料 价格 二手猫屋 80元 铰链 15元 灯带 20元 esp32 15元 总结 这套A1封箱方案是我觉得比较满意的，在满足需求目标的同时、且价格合适，且又带来DIY改造的乐趣。\n","date":"2025-09-25T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-09-25","permalink":"https://zfj1441.com/p/%E6%8B%93%E7%AB%B9a1%E5%B0%81%E7%AE%B1%E5%AE%9E%E8%B7%B5/","title":"拓竹A1封箱实践"},{"content":"前言 四月份，我在嘉立创完成了自己画原理图、设计PCB并免费打板的初体验初次体验嘉立创电路设计PCB打板。但对于更复杂的电路，我的水平还远远不够。好在有立创开源平台，可以让我学习并复刻大神们的设计。 在平台闲逛许久，一直因技术门槛而犹豫。直到发现《基于esp32的有线蓝牙网关》这个项目，它相对简单，又正好是我所需要的，于是决定大胆尝试，开启我的第一次复刻之旅。\n正文 前期准备 开源项目大多使用贴片元器件，用电烙铁逐个焊接非常吃力。为此，我特地“斥巨资”采购了一套贴片焊接装备：恒温加热台、硅胶垫、焊锡膏和精密镊子。\n在嘉立创“薅羊毛” 1. 下单PCB 原则很简单：收费项目一概不选，只选免费的。最后使用优惠券完成了PCB打样，还顺手用另一张券兑换了3D打印的外壳（虽然我自己有3D打印机，但有免费券不用总觉得亏了）。\n2. 下单元器件 这是整个流程中最繁琐的一步。通过BOM清单下单，系统能自动匹配大部分元器件，但总有几样无法识别，需要对照电路图手动查找型号并替换。\n此外，还需注意：\n最小起订量（MOQ）： 有些元件单价几分钱，但起订量要1000个。遇到这种情况，我会根据价格决定是在立创硬着头皮买，还是转战淘宝、拼多多。\n发货地：立创有广州和江苏两个仓库。理想情况是所有元件集中从一个仓库发货以节省邮费。但现实很骨感——我优先选择广州仓后，发现有两种总价仅7毛钱的元件只有江苏仓有货。这意味着我要为这7毛钱另付16元邮费。最终，我果断放弃了这两种元件，选择在其他平台单独购买\n3. 下单3D打印 这一步最简单，上传模型，用券，下单，一气呵成。\n开工：贴片焊接 上锡膏：将锡膏均匀涂抹在焊盘上。\n摆元件：用镊子根据PCB上的位号小心翼翼地将元器件摆放到位。这里要特别留意二极管、电容等有正负极性的元件，方向绝不能错。\n加热焊接：将加热台升温至200℃，放上PCB，静静等待奇迹发生……几分钟后，一块焊接完成的板子就出炉了！\n刷系统固件 通过板载的TTL接口刷写固件。这里我遇到了一个大坑：刷机软件一直报错“2-sync fail”，折腾了将近四个小时。最后才发现，问题出在芯片供应商——他们“挂羊头卖狗肉”，把ESP32芯片冒充ESP32-S系列卖给了我，导致固件无法正常识别和刷入。\n应用调试 解决刷机问题后，进入最终的通电测试。幸运的是，第一次做贴片焊接，板子没有“冒烟放烟花”。主要功能测试一切正常，唯独两个LED指示灯不亮。鉴于核心功能完好，我也就懒得深究，算是留下一点小小的遗憾。\n总结 我的首次立创开源项目复刻，总体还算成功！通过这次实践，我不仅学会了如何高效地利用嘉立创的免费资源（PCB和3D打印），还掌握了根据BOM清单采购元件、使用加热台进行回流焊等新技能。\n参考 嘉立创-基于esp32的有线蓝牙网关\n百度贴吧-arduino吧\n","date":"2025-09-15T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-09-15T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E7%AC%AC%E4%B8%80%E6%AC%A1%E5%A4%8D%E5%88%BB%E7%AB%8B%E5%88%9B%E5%BC%80%E6%BA%90%E5%B9%B3%E5%8F%B0%E9%A1%B9%E7%9B%AE/","title":"第一次复刻立创开源平台项目"},{"content":"前言 上周通过makerworld打印了个宝塔氛围灯，但是打完后才知道这玩意还需要拓竹Kit-001配件（彩色灯）才算真的一套摆件。但看着32元价格，思索片刻决定自己DIY做一个。一是因为觉得这配件功能简单，32元价格有点虚高；二是因为家里有智能家居homeassistant，我需要氛围灯能接入实现设备间联动。\n正文 最初想法只是点亮宝塔氛围灯，所以找出个ESP-01 Relay，搭配几个LED暖灯。哐当哐当\u0026hellip;..几个小时搞定了，但看着只能点亮和熄灭，感觉这“氛围灯”少了那么点意思，所以决定再升级下，弄个完整版的RGB灯。翻翻抽屉找到个Wemos D1-mini，采购个5v的RGB灯带（WS2812B）三色灯带，再刷上WLED系统妥妥的。\n电路图 给灯带设计个支撑 刷上WLED系统 \u0026hellip;\u0026hellip; (忘记截图了)\n最终效果 成本核算 名称 单价 消耗 D1 mini 8.5元/个 1个 WS2812B灯带 8.34元/米 0.5米 总费用 约13元 总结 从电路图到刷固件再到3D建模，最后完整装配调试，挺有意思的小玩具。\n参考 WLED Project\nD1 mini手册\n","date":"2025-09-07T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-09-07T22:37:26+08:00","permalink":"https://zfj1441.com/p/3d%E6%89%93%E5%8D%B0%E4%BB%B6-%E6%B0%9B%E5%9B%B4%E7%81%AF%E6%94%B9%E9%80%A0/","title":"3D打印件-氛围灯改造"},{"content":"前言 周四（7月24）买了台拓竹A1 comb打印机，这应该算是一次冲动消费，因为从想买到下单仅用了一周的时间。期间询问从事3d打印工作的建议、以及调研了TB、JD、PDD价格最后2500入手。\n正文 着急的等了两天，在周六收货。到手后按说明书一步步操作，完成设备安装、调试，最后使用附赠的耗材打印了第一个模型是：3d小船，就此开启我的3D打印世界。\n周末新买的耗材陆续收获，从此进入疯狂打印模式：从A1自用工具、生活用品、儿童玩具到动漫模型，在mark world上看到有意思有趣的玩意通通都打上一遍。\n然而好景不长，第三天就发现机器突然打不了。着急的我又是百度、又是看说明书、最终在客服的帮助下找到了原因，料板卡料导致进料失败。拉出耗材剪去最前面一段重新上料，问题解决。在之后的打印过程中，卡料真的太普遍了，但是有了上次的经验，处理起来显得确得心应手些。\n总结 3d打印机是非必要的物品，但拥有后确实给我们全家带来非常多乐趣。1）大人的梦中玩具：小时候听过神笔马良的故事，3d打印或多或少有点这个意思，把想想中的物品通过3d建模到打印成实物，这确实是一件让人非常兴奋的事。2）从建模 -\u0026gt; 打印 -\u0026gt; 收获实物，整个过程中正好对应《上瘾模型》，让人欲罢不能。3）对于折腾佬来说，从购买3d打印机后，一切折腾才刚开始。什么设备优化、封箱、3d建模、DIY设备等，源源不断的折腾项目。4）小朋友玩具制造机。\n","date":"2025-08-13T00:00:00Z","image":"https://image.vitshare.cn/blog/content/post/第一台3D打印机/title.jpg","permalink":"https://zfj1441.com/p/%E7%AC%AC%E4%B8%80%E5%8F%B03d%E6%89%93%E5%8D%B0%E6%9C%BA/","title":"第一台3D打印机"},{"content":"上半年赶鸭子上架搞了个数据建模，摸索过程中搜到的一些资料，记录下\n[实用教程]使用Python绘制SCI论文中的AUC、AUPR曲线图\n风控模型指标全解：KS、LIFT、GINI等\n分类模型的评价指标\u0026ndash;混淆矩阵，ROC，AUC，KS，Lift，Gain\nPython实现LightGBM分类模型(LGBMClassifier算法)项目实战\n二分类、多分类、回归任务，一个项目get竞赛必备模型\nQLMX/data_mining_models数据挖掘类比赛常用模型\n【Python机器学习】 三分钟读懂ligntGBM模型可视化及预测效果的ROC曲线评价\n【代码实战-个人笔记】混淆矩阵绘制\u0026ndash;随机森林分类/XGBoost分类/LightGBM分类/CatBoost分类|分类模型比较\n【Pytorch】进阶学习：深入解析 sklearn.metrics 中的 confusion_matrix（混淆矩阵）\n【小白-统计基础】 - 2 - KS检验\n数据挖掘建模评价指标-KS指标的python代码实现的两种方法 怎么用python实现一个数据集的KS\n两个变量的数据怎么进行显著性分析检验\n金融消费产品单变量分析\n单变量分析-首逾率走高分析 机器学习： LightGBM模型（优化版）——高效且强大的树形模型\nLightGBM回归模型优化实践：提升预测精度的策略\n【机器学习】：Xgboost/LightGBM使用与调参技巧\nLightGBM+gridsearchcv调参\nLightGBM参数详解：提升模型性能的关键\n","date":"2025-07-23T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-07-23","permalink":"https://zfj1441.com/p/python%E5%BB%BA%E6%A8%A1%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99/","title":"Python建模学习资料"},{"content":"前言 不知道从什么时候开始网站的免费证书从一年一更新改成三个月。我的三个域名证书不是同一时间申请，所以时不时就收到证书过期提醒。上周又一域名证书需要更新。趁周末通过acme.sh彻底解决域名证书更新的烦恼，在这做个记录顺便分享给和我有同样烦恼的朋友。\n正文 acme.sh 是一个开源的 ACME 协议客户端，主要用于自动化申请、更新和管理 Let\u0026rsquo;s Encrypt 或其他兼容 ACME 协议的 SSL/TLS 证书。它的核心功能是简化 HTTPS 证书的获取和续签流程，特别适合在服务器或嵌入式设备上使用。\nacme.sh使用步骤\nacme.sh安装 git clone https://github.com/acmesh-official/acme.sh.git cd ./acme.sh ./acme.sh --install -m my@example.com #申请证书时使用的邮箱 vi ~/.bash_profile alias acme.sh=~/.acme.sh/acme.sh source ~/.bash_profile 选择证书验证方式 acme.sh提供多种证书验证方式，这里推荐使用WebRoot文件验证方式\n#切换使用Let\u0026#39;s Encrypt的免费证书 acme.sh --set-default-ca --server letsencrypt # 手动指定要申请的域名和webroot路径 acme.sh --issue -d zfj1441.com -d www.zfj1441.com --webroot /home/zfj/www/ 安装证书 # --install-cert 参数可以分别指定 key 和 证书文件需要安装到的目录路径 # --reloadcmd 指定在证书安装完成后所需要执行的指令 acme.sh --install-cert -d zfj1441.com \\ --key-file ~/etc/nginx/key.pem \\ --fullchain-file ~/etc/nginx/cert.pem \\ --reloadcmd \u0026#34;systemctl reload nginx\u0026#34; 因我使用非root用户安装aceme.sh，执行以上脚步时需验证root密码，手动执行自然没问题，但是如果定时任务可不行。所以需要给当前用户免验证root密码\nvi /usr/share/polkit-1/actions/org.freedesktop.systemd1.policy \u0026lt;action id=\u0026#34;org.freedesktop.systemd1.manage-units\u0026#34;\u0026gt; ....省略多行内容 \u0026lt;defaults\u0026gt; \u0026lt;allow_any\u0026gt;yes\u0026lt;/allow_any\u0026gt; \u0026lt;allow_inactive\u0026gt;yes\u0026lt;/allow_inactive\u0026gt; \u0026lt;allow_active\u0026gt;yes\u0026lt;/allow_active\u0026gt; \u0026lt;/defaults\u0026gt; \u0026lt;/action\u0026gt; #重启policy systemctl restart policy 定时更新证书 安装acme.sh后，会自动在当前用户的定时脚步中加入以下任务\ncrontab -l 11 1 * * * \u0026#34;/home/zfj/.acme.sh\u0026#34;/acme.sh --cron --home \u0026#34;/home/zfj/.acme.sh\u0026#34; \u0026gt; /dev/null #手动验证定时任务 /home/zfj/.acme.sh/acme.sh --cron --home \u0026#34;/home/zfj/.acme.sh\u0026#34; --force 其他 # 查看证书 acme.sh --list #指定证书的详细信息 acme.sh --info -d zfj1441.com 总结 再也不想收到域名证书超时的提醒了，趁周末花一个小时吧所有域名都上了acme.sh实现自动域名证书更新，一劳永逸。\n参考 acmesh-official/acme.sh\n正确使用 acme.sh， 让你的网站永久使用 ssl 证书，It\u0026rsquo;s free!\n使用 acme.sh 进行SSL证书的申请和自动更新\nLinux - 非root用户使用systemctl管理服务\n","date":"2025-07-20T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-07-20","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8acme.sh%E8%A7%A3%E5%86%B3%E7%83%A6%E4%BA%BA%E7%9A%84%E8%AF%81%E4%B9%A6%E6%9B%B4%E6%96%B0/","title":"使用acme.sh解决烦人的证书更新"},{"content":"前言 最近在WPS的Excel中通过JSA（JavaScript for Applications）成功开发了一套自动化工具，对重复性数据处理工作有显著提升。本文将总结开发过程中的关键步骤，帮助初学者快速上手JSA自动化。\n正文 Sheet工作表操作 let sh1 = Application.ActiveSheet // 取活动的sheet let sh2 = Sheets(\u0026#39;sheet1\u0026#39;) // 根据名称取sheet let sh3 = Sheets(1) // 根据索引号取sheet let sh4 = Sheets.Add() sh4.Name = \u0026#34;new Sheet\u0026#34; 单元格操作与数据遍历 let uname = sh1.Cells(1,6).Value() // 只读 let uname1 = sh1.Cells(1,6).Value2 // 可读写 Cells.Item(1,2).Value2 = “hello” // 单元格赋值 Cells.Item(1,2).Formula = “=round(100/3,2)” // 公式 sh1.Cells(targetRow, col).Interior.Color = 0x00FF00; // BGR格式的绿色 // 遍历 let sh1=Application.ActiveSheet let rowCount=sh1.UsedRange.Rows.Count for(var i=rowCount;i\u0026gt;=1;i—){ } 输入输出交互 let input = InptBox(\u0026#34;提示信息\u0026#34;,\u0026#34;表格名称\u0026#34;,\u0026#34;默认值：123\u0026#34;) MsgBox(\u0026#34;执行完成\u0026#34;) Console.log(\u0026#34;打印内容\u0026#34;) 工具界面 总结 平时工作中大部分都是vlookup解决问题，透视表解决问题。这次为长期工作的表格用jsa开发一套自动化处理工具集合，大大缩短数据处理时间。最后配合界面做几个按钮，一套流程下来所有数据都处理好，非常nice。\n参考 WPS JSA 宏编程（JS）：1.初识\nWPS JS宏有开发文档吗？\nWPS的JS宏操作方法总结大全\nWPS宏编程-JS宏常用基础操作\nWPS开放平台：开发文档\n","date":"2025-07-18T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-07-18","permalink":"https://zfj1441.com/p/wps%E4%B8%ADjsa%E8%87%AA%E5%8A%A8%E5%8C%96%E5%BC%80%E5%8F%91%E5%88%86%E4%BA%AB/","title":"WPS中JSA自动化开发分享"},{"content":"前言 最近骑小电驴次数多了，常需要在车库里充电，这一冲就是一晚上。而且车库常年累月堆了一堆杂物，难免会让人产生失火的担忧。毕竟本小区2018年就有一次案例。因为有以上顾虑，所以想着通过homeassistant做些安全措施，其中一项就是监控告警。\n正文 整体方案是在车库里增加温度传感器，homeassistant自动化检测温度传感器，发现长时间高温，则发送预警通知。\n1. 注册qq渠道消息qmsg 2. configuraction.yaml配置 notify: - name: qmsg platform: rest resource: https://qmsg.zendee.cn/send/你的token method: POST data: msg: \u0026#34;HASS提醒:\\n时间{{ states(\u0026#39;sensor.date_time\u0026#39;) }} \\n {{ message }}\u0026#34; qq: 接受者QQ号 3. 应用调试 测试调用\n自动化配置\n4. 效果展示 总结 消息通知随处都有需要，这里以qmsg配置预警通知举例做个简单的教程以备后续所需。\n参考 homeAssistant官方文档\n超简单微信通知\nqmsg使用文档\n","date":"2025-06-29T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-06-29","permalink":"https://zfj1441.com/p/homeassistant%E9%85%8D%E7%BD%AEqq%E6%B6%88%E6%81%AF%E9%80%9A%E7%9F%A5/","title":"homeassistant配置qq消息通知"},{"content":" 上瘾模型（the Hook Model）：触发——行动——多变的酬赏——投入。 第一步就是引发用户去使用你的产品，这叫作“触发”。 触发之后，第二步就是行动。行动要兼具动机和能力，有了动机，还需要用户的能力足够完成行为。 行动之后，要给用户酬赏，还得是多变的酬赏。所谓多变的酬赏，就是指酬赏要有不可预期性。 最后，是让用户在产品上进行越来越多的“投入”。用户与产品亲密接触得越多，就越离不开它。 通过用户的“投入”，就可能产生下一次“触发”，从而开始一个正向循环。 于是你就上瘾了（hooked）。\n\u0026mdash;-摘录来自 上瘾：让用户养成使用习惯的四大产品逻辑 [美]尼尔·埃亚尔,[美]瑞安·胡佛\n看完本书后，非常有感触。原来自己情不自禁上瘾都是某些人的精心设计与安排。\n","date":"2025-06-12T20:53:19+08:00","image":"https://picsum.photos/800/600?r=2025-06-12","permalink":"https://zfj1441.com/p/%E4%B8%8A%E7%98%BE%E8%AF%BB%E5%90%8E%E6%84%9F/","title":"《上瘾》读后感"},{"content":"前言 事情起因是在网上看到说可以免费撸华为云电脑window10系统还能破时长限制。等我按教程一步步操作时，才发现免费的车早已开走，只剩下一屁股尾气。但我没有就此打住而是继续探索，最后虽错过免费windows10云电脑，但可撸台4c8G云服务器，分享给有需要的朋友。\n正文 注册华为账号 打开【华为云开发者空间】，登录你的华为账号（如果没有账号的自己去注册）；\n开通云主机 在\u0026quot;工作台\u0026quot;-\u0026gt;\u0026ldquo;我的云主机”，会看到系统给每位开发者免费赠送一台云主机电脑，每年能用180小时（基础会员全年总时长为180小时，您可分12次申请，每次15小时。当您的云主机使用时长小于等于60分钟时，可点击“更多”按钮申请延时）。继续点“配置主机”； 安装内网穿透 我们申请的云主机没有公网IP无法远程连接，需要借助内网穿透工具实现外网访问。穿透方法有很多，这里以frp为例子。\n在页面点击\u0026quot;打开云主机\u0026rdquo; -\u0026gt; “进入桌面\u0026quot; -\u0026gt; 右上角X -\u0026gt; 等待进入桌面后，右键“Open Terminal Here\u0026quot;，然后参考【stilleshan/frpc 】安装frpc\n#切换至超级管理员 sudo su - #修改developer密码 passwd developer #安装frpc cd /tmp wget https://ghfast.top/https://raw.githubusercontent.com/stilleshan/frpc/master/frpc_linux_install.sh \u0026amp;\u0026amp; chmod +x frpc_linux_install.sh \u0026amp;\u0026amp; ./frpc_linux_install.sh # 修改 frpc.toml 配置 vi /usr/local/frp/frpc.toml serverAddr = \u0026#34;frp.freefrp.net\u0026#34; serverPort = 7000 auth.method = \u0026#34;token\u0026#34; auth.token = \u0026#34;freefrp.net\u0026#34; [[proxies]] name = \u0026#34;yourname_linux_ssh\u0026#34; #必须修改，且不能与其他用户重复 type = \u0026#34;tcp\u0026#34; localPort = 22 remotePort = 22222 #10001-50000 随机选，确保不要与其他用户重复 sudo systemctl restart frpc # 重启 frpc 服务即可生效 #远程连接 ssh -p 22222 developer@frp.freefrp.net 在frpc配置中我们使用了免费的frps服务，如果您有自己的公网服务器也可以参考【stilleshan/frps 】安装自己的frps服务\n卡BUG 确保ssh能远程之后，在网页端选择“关机”-\u0026gt;“确认\u0026quot;。此时云主机的时长就不会计算，但云主机其实还在运行。 参考 华为云免费撸云电脑主机改装Windows10系统并破解在线时长系统永久在线\nfatedier/frp\nstilleshan/frpc\nstilleshan/frps\n免费frp穿透服务器\n","date":"2025-05-29T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-05-29","permalink":"https://zfj1441.com/p/%E5%85%8D%E8%B4%B9%E6%92%B8%E5%8F%B0%E5%8D%8E%E4%B8%BA4c8g%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8/","title":"免费撸台华为4c8G云服务器"},{"content":"前言 最近行车记录仪坏了，启动后无法正常录像。掐指一算也用了8、9年了，是该换个新设备了。JD逛了一圈选择了带24小时监控的行车记录仪，但问题是如何取电。之后逛了一圈论坛大致是有了点方向，记录下以免已有有需要。\n正文 参考 XR-V车型无损快速取电\n本田XRV缤智行车记录仪保险盒ACC常电接线方法及图解\n请高手在XRV保险盒里找个常电，Acc\u0026hellip;,倒车检测线？\n","date":"2025-05-12T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-05-12","permalink":"https://zfj1441.com/p/%E6%9C%AC%E7%94%B0xrv%E7%BC%A4%E6%99%BA%E8%A1%8C%E8%BD%A6%E8%AE%B0%E5%BD%95%E4%BB%AA%E4%BF%9D%E9%99%A9%E7%9B%92acc%E5%B8%B8%E7%94%B5/","title":"本田XRV缤智行车记录仪保险盒ACC常电"},{"content":"前言 家里热水器控制太麻烦，一直想通过diy接入homeassistant。经过长达半年学习，总是慢慢入门。今天花几个小时画原理图、pcb设计，顺利下单并且还是0元购。\n正文 功能：利用开关三极管实现3.3v控制5v电路\n嘉立创渲染效果： 20250422更新 实物图 当拿到成品板子，又兴奋又有成就感。板子的质量非常棒，拿在手里感觉比我热水器的板子质量还高。另外就是原理图，1）经过测试得把R2、R4、R6摘除才能起作用。2）可能是因为没有加电容，偶尔手碰到会导致板子重启。至于为啥我\u0026hellip;\u0026hellip;.对就是这样\u0026hellip;\u0026hellip;. 系统 因为采用按钮模拟方式控制，所以无法读取到正确的热水器温度。所以采用程序计算的方式，当然如果物理按键上加减了温度，系统也无法识别。\nglobals: - id: my_global_int type: int restore_value: no initial_value: \u0026#39;35\u0026#39; button: - platform: output name: \u0026#34;power Button\u0026#34; output: output1 duration: 200ms - platform: output name: \u0026#34;Sub Button\u0026#34; output: output2 duration: 200ms on_press: then: - lambda: |- if (id(my_global_int) \u0026gt; 50 ) { id(my_global_int) -= 5; }else if (id(my_global_int) \u0026gt; 35 \u0026amp;\u0026amp; id(my_global_int) \u0026lt;= 50) { id(my_global_int) -= 1; }else { } - platform: output name: \u0026#34;Add Button\u0026#34; output: output3 duration: 200ms on_press: then: - lambda: |- if (id(my_global_int) \u0026gt;= 50 \u0026amp;\u0026amp; id(my_global_int) \u0026lt; 65) { id(my_global_int) += 5; }else if (id(my_global_int) \u0026lt; 50) { id(my_global_int) += 1; }else { } output: - platform: gpio pin: 14 id: output1 - platform: gpio pin: 16 id: output2 - platform: gpio pin: 13 id: output3 sensor: - platform: template name: \u0026#34;Template Sensor\u0026#34; unit_of_measurement: \u0026#34;°C\u0026#34; icon: \u0026#34;mdi:water-percent\u0026#34; device_class: \u0026#34;temperature\u0026#34; state_class: \u0026#34;measurement\u0026#34; lambda: |- return id(my_global_int); update_interval: 2s 总结 嘉立创还是牛，这么专业的软件做的如此简单。设计电路、pcb设计、打板一条龙服务。重点还是免费0元购。\n参考 能率燃气热水器智能化改造\nbilibili：小白入门-如何使用立创EDA设计一个简单的PCB\nbilibili：三极管开关电路限流电阻怎么选取\n","date":"2025-04-12T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-04-12","permalink":"https://zfj1441.com/p/%E5%88%9D%E6%AC%A1%E4%BD%93%E9%AA%8C%E5%98%89%E7%AB%8B%E5%88%9B%E7%94%B5%E8%B7%AF%E8%AE%BE%E8%AE%A1pcb%E6%89%93%E6%9D%BF/","title":"初次体验嘉立创电路设计PCB打板"},{"content":" 前言 描述下我为什么选择看这本书：一是发现身边朋友心理问题，二是看到有别看推荐该书，同时书名的别出心裁，三是当睡前读物，避免刷短视频。\n正文 《蛤蟆先生去看心理医生》是英国心理学家罗伯特·戴博德（Robert de Board）创作的大众心理学读物，以童话《柳林风声》中的角色蛤蟆先生为主角，讲述了他通过心理咨询走出抑郁、实现心理成长的故事。\n其中影响深刻的是，苍鹭描述的三种自我状态：\n儿童自我状态：受童年情绪影响，行为模式像孩子一样（如依赖、讨好）。 父母自我状态：内化了父母的挑剔或控制，对自己或他人严苛。 成人自我状态：理性、独立，能基于现实做出决策。\n这三种状态可以映射生活中的人。 总结 看完后，自我审视一番。却是很多时候遇到困难总会想谁能帮你处理，遇到问题我该怎么办。这不就是儿童自我状态 ，习惯依靠他人解决问题。学会自我独立思考、解决困难独当一面。\n","date":"2025-03-31T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-03-31","permalink":"https://zfj1441.com/p/%E8%9B%A4%E8%9F%86%E5%85%88%E7%94%9F%E5%8E%BB%E7%9C%8B%E5%BF%83%E7%90%86%E5%8C%BB%E7%94%9F%E8%AF%BB%E5%90%8E%E6%84%9F/","title":"《蛤蟆先生去看心理医生》读后感"},{"content":"前言 最近逛bbs.hassbian.com论坛，看到有位大佬通过esp32把浴霸触摸开关接入homeassistant，实现家庭控制。正好家里有个常年落灰的触摸台灯，可以让我这电子小白试一试。\n正文 刚开始觉得会很顺利，毕竟有成功的触摸开关接入的案例，然而过程还是有些曲折。\n电路原理分析 主控使用的是一颗DLT8T02。啥也不懂，直接问deepseek。 从问的知识范畴内理解pcb大致功能：\n电源降压模块，把12v降压到主控芯使用的电压 DLT8T02主控芯片，实现一路触摸开关，一路pwm控制 放大电路，对pwm信号放大，最终可以实现4档调光 改造思路 按浴霸触摸开关改造的成功案例，设计的初步改造思路：\n在触摸开关处串联二极管后接入esp8266 通过监测pcb中的led灯亮与不亮判断台灯工作状态。led亮：非工作状态，led不亮：工作状态 经过测试发现，我这台灯触摸开关接二极管无效，任何金属接触都能触发。无奈继续翻资料、看视频了解触摸开关原理：\n新改造思路：\n在触摸开关引出导线，使用继电器控制导线接地的通断 。 通过监测pcb中的led灯亮与不亮判断台灯工作状态。led亮：非工作状态，led不亮：工作状态 最后成品 esphome核心配置\nbutton: - platform: output name: \u0026#34;kaiguan\u0026#34; output: output1 duration: 1200ms - platform: output name: \u0026#34;qiehuan\u0026#34; output: output1 duration: 500ms output: - platform: gpio pin: 0 id: output1 inverted: True binary_sensor: #状态反馈，io接输出引脚 - platform: gpio pin: number: 3 #RX mode: INPUT_PULLUP name: \u0026#34;zhuangtai\u0026#34; 总结 第一次DIY改造触摸开关，结果失败。1）esphome远程控制和触摸开关双控台灯。但pcb装回原位后发现触摸开关（触摸开关和手指中间有熟料外壳）灵敏度大大降低，几乎到了无法使用的地步。2）从抽屉翻出来的降压模块尺寸问题导致台灯底座无法正常合拢。改造结果虽然失败，但是从改造历程中收获挺多。\n参考 用8266和11个二极管魔改28元触摸浴霸开关，接入hass\n能率燃气热水器智能化改造\n","date":"2025-03-29T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-03-29","permalink":"https://zfj1441.com/p/%E8%A7%A6%E6%91%B8%E5%8F%B0%E7%81%AFdiy%E6%8E%A5%E5%85%A5homeassistant/","title":"触摸台灯DIY接入homeassistant"},{"content":"前言 在日常办公中总有些每天必完成的动作，比如：登陆即时聊天、登陆OA门户、每日系统巡检、报表下载通报等，一些工作简单但必做的事。最近在UOS系统下找到个合适的解决方案：xdotool。它类似windows下的按键精灵，通过模拟鼠标键盘的操作实现自动化。对我而言重要一点是：UOS系统默认安装xdotool，经过几天了解分享下xdotool基本使用技巧。\n正文 模拟键盘输入、快捷键 xdotool type \u0026#34;hello world!\u0026#34; xdotool type \u0026#34;hello \\n world!\u0026#34; #多行文本 xdotool key a xdotool key ctrl+alt+t #模拟快捷键 xdotool key Return #回车 模拟鼠标操作：移动鼠标、点击、拖动等 xdotool click 1 #左键单击 xdotool click 2 #鼠标中键 xdotool click 3 #右键单击 xdotool click 4 #滚轮向上 xdotool click 5 #滚轮向下 xdotool click --repeat 2 1 #左键双击 xdotool mousedown 1 mousemove 600 600 mouseup 1 #左键拖动 xdotool getmouselocation #当前鼠标位置 xdotool mousemove 100 100 click 1 #移动到指定位置单击 xdotool mousemove_relative 10 10 #鼠标相对位置移动 窗口管理：查找窗口、激活窗口、移动、调整大小等 xdotool search --name \u0026#34;Firefox\u0026#34; windowactivate #查找窗口并激活第一个窗口 xdotool getactivewindow windowsmove 100 100 #移动最前端窗口到（100，100） xdotool getactivewindow windowsize 800 600 #设置最前端窗口大小800x600 xdotool脚步文件 #!/usr/bin/xdotool search —class=“Goole Chrome” windowactivate gateactivewindow windowsize 100% 100% key ctrl+t key ctrl+l type “https://vitshare.cn” key Return 运行脚本：./scriptfile\nuos配置自动快捷方式启动\n设置-\u0026gt;键盘和语言-\u0026gt;快捷键，添加自定义快捷键。 总结 总之，xdotool 是一个功能强大且灵活的工具，适合在 X11 环境下进行各种自动化操作，释放你的双手。\n参考 xdotool开源代码\n发现一个xdotool，是个神器\nlinux 下的按键精灵 xdotool\nshell脚本：在Linux中模拟击键和鼠标移动, 键盘精灵, xdotool 模拟用户交互\n","date":"2025-03-16T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-03-16","permalink":"https://zfj1441.com/p/%E5%8F%91%E7%8E%B0%E4%B8%80%E4%B8%AA%E9%87%8A%E6%94%BE%E5%8F%8C%E6%89%8B%E7%9A%84%E5%B7%A5%E5%85%B7xdotool/","title":"发现一个释放双手的工具xdotool"},{"content":"前言 最近追剧斗破苍穹、白夜追凶二都是实时更新的剧，看着时间点就上各大网站或论坛搜索最新链接下载然后再用电视、手机、pad观影。在智能化时代想着怎么能实现自动化下载\n正文 第一种方案： 通过群晖web api创建Download Station下载任务\n可参考：\n利用群晖Download Station的webapi自动化下载任务 hiddenblue/SynoAutoDown\n第二种方案： 利用Download Station RSS订阅功能实现自动下载\n可参考：\n自建RSS源让群晖自动下载我们想要的资源\n群晖设置自动获取电影下载地址并自动添加下载\n自动追更 下载动画片/电视剧，通过RSS订阅追番追剧\n当然为了提高下载效率还需要自动添加tracker\n群晖 Download Station BT 种子下载 自动添加trackerlist\n参考 利用群晖Download Station的webapi自动化下载任务\nSynology Download Station Official API\n自建RSS源让群晖自动下载我们想要的资源\n群晖设置自动获取电影下载地址并自动添加下载\n群晖 Download Station BT 种子下载 自动添加trackerlist\nhiddenblue/SynoAutoDown\n自动追更 下载动画片/电视剧，通过RSS订阅追番追剧\n","date":"2025-02-10T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-02-10","permalink":"https://zfj1441.com/p/%E6%8E%A2%E7%B4%A2%E7%BE%A4%E6%99%96downloadstation%E8%87%AA%E5%8A%A8%E4%B8%8B%E8%BD%BD/","title":"探索群晖DownloadStation自动下载"},{"content":"前言 在工作中常看到一些windows嵌入式B/S系统，开机后自动启动web应用并实现全屏。正好我用小米平板2做智能家居homeassistant也有同样的需求。网上了解下个实现方式，记录下顺便做个分享。\n正文 创建bat脚步，实现全屏启动chrome并全屏访问网站，并为bat脚步创建快捷方式 @echo off if \u0026#34;%1\u0026#34; == \u0026#34;h\u0026#34; goto begin start mshta vbscript:createobject(\u0026#34;wscript.shell\u0026#34;).run(\u0026#34;%~nx0 h\u0026#34;,0)(window.close)\u0026amp;\u0026amp;exit :begin echo \u0026#34;use ping to delay\u0026#34; set SLEEP=ping 127.0.0.1 /n echo %time% %SLEEP% 10 \u0026gt; nul echo %time% @start chrome.exe -kiosk \u0026#34;http://www.baidu.com\u0026#34; Win+R打开运行，然后宿输入：shell:startup，弹出windows启动目录文件夹，将刚才建立的快捷方式拖入文件夹\n重启windows系统就可以实现自启动chrome并全屏访问www.baidu.com\n参考 Windows开机自动打开谷歌浏览器的一个网页，并且全屏显示\nwindows cmd命令行隐藏窗口后台启动运行程序,开机自启\nwindows bat脚本 后台运行目标exe\n","date":"2025-01-03T00:00:00Z","image":"https://picsum.photos/800/600?r=2025-01-03","permalink":"https://zfj1441.com/p/%E5%AE%9E%E7%8E%B0windows%E5%BC%80%E6%9C%BA%E5%90%AF%E5%8A%A8chrome%E5%B9%B6%E5%85%A8%E5%B1%8F%E8%AE%BF%E9%97%AE%E6%9F%90%E7%BD%91%E7%AB%99/","title":"实现windows开机启动chrome并全屏访问某网站"},{"content":"回顾2023年12月31日，有着家人作伴，今年的12月31日跨年相比有些冷清。不过也好整理下2024年的过往，收拾下2024年的残局，展望下2025年。\n生活时间线 2024年2月和家人游葛仙山\n2024年3月带爸妈体验大学生活\n2024年3月带爸妈游庐山住宾馆\n2024年3月偷偷卸掉自行车小轮，自行车轻松拿捏\n2024年5月人山人海的南昌秋水广场\n2024年7月山沟沟里找到个避暑乘凉好地方\n2024年9月趁周末好天，弋阳龟峰轻松拿捏\n2024年11月带儿子参观南昌航空展\n2024年12月夜万寿宫签到打卡\n任务目标 计划看的书才看才完成1/4 每季度3万步，应该是完成了。但体重真大越来越夸张了 经过半年的坚持分享公众号有一点收获。但又面临新的问题，没有新话题，急需新血液注入 下一目标 控制体重首要任务，运动太难了。控制下饮食少吃点吧。 重温三国演义、楚汉传奇 持续坚持公众号/博客内容分享，寻找新鲜血液 继续2024看书计划 ","date":"2024-12-31T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-12-31","permalink":"https://zfj1441.com/p/2024%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/","title":"2024年终总结"},{"content":"前言 2018年买的腾讯云服务器马上就要到期了，续费是不可能的。之后也考虑过用闲置玩客云+家庭网搭建博客系统，经过几个月折腾勉强能用。近几天网上发现华为云有活动，一波操作下来41元2年3个月2c1G云服务器到手，感觉还是挺划算特来分享。\n购买方式 注册华为云并认证（新户） https://developer.huaweicloud.com 加入华为云沃土计划，免费领取400-500代金卷 https://developer.huaweicloud.com/programs/dev-program.html 购买特价Flexus服务器 ，这里我选择了29元1年的云服务器https://activity.huaweicloud.com/ecs.html 使用优惠劵续费2次，一次9个月、一次6月（续费时请选择1年以内，续费1年以上时长支付时无法选择优惠劵） 总费用：一年服务器29元，最后一次续费11.95元，总价40.95元。 最后 以上买方式截止2024年11月28日亲测有效。双11华为云的一波福利，有刚需的可以试试。\n","date":"2024-11-27T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-11-27","permalink":"https://zfj1441.com/p/%E5%8D%8E%E4%B8%BA%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A82%E5%B9%B43%E4%B8%AA%E6%9C%88%E4%BB%85%E9%9C%8041%E5%85%83/","title":"华为云服务器2年3个月，仅需41元"},{"content":"前言 用闲置玩客云替代云服务器做搭建博客系统，今天继续分享迁移waline留言系统。由于玩客云CPU架构的原因docker pull拉不到镜像，所以只能自己构建waline玩客云专用镜像，可以参考记录下docker构建镜像。之后就是迁移数据， 原系统用了postgresql数据，为了方便后期管理迁移过程中顺便把数据库换成sqlite3。主要操作如下：\n玩客云部署waline # 构建docker镜像 git clone https://github.com/walinejs/waline.git cd waline docker build -t lizheming/waline -f packages/server/Dockerfile . # 基于sqlite的waline mkdir -p /root/waline cd /root/waline \u0026amp;\u0026amp; wget https://raw.githubusercontent.com/walinejs/waline/refs/heads/main/assets/waline.sqlite docker run -d \\ -v /root/waline:/app/data \\ -e SQLITE_PATH=/app/data \\ -e JWT_TOKEN=xxxxxxx \\ -e TZ=Asia/Shanghai \\ -p 127.0.0.1:8360:8360 \\ --name=waline \\ lizheming/waline 数据迁移 postgresql导出csv #登录psql psql -U postgres -d postgres -h 172.17.0.1 -p 5432 COPY (select * from wl_comment) to \u0026#39;/tmp/wl_comment.csv\u0026#39; with ( format csv); COPY (select * from wl_counter) to \u0026#39;/tmp/wl_counter.csv\u0026#39; with ( format csv); COPY (select * from wl_users) to \u0026#39;/tmp/wl_users.csv\u0026#39; with ( format csv); sqlite3导入csv sqlite3 waline.sqlite3 .mode csv .import /path/to/yourfile.csv your_table #基本命令 .help .exit .databases .schema .tables 其他指令 # postgresql导出insert语句 pg_dump -U postgres -h 127.0.0.1 -t wl_comment --column-inserts --data-only -F p postgres \u0026gt; output.sql #posgresql执行sql文件 psql -U postgres -d postgres -h 172.17.0.3 -p 5432 -f xx.sql 参考 waline快速上手\n","date":"2024-11-16T17:37:26+08:00","image":"https://picsum.photos/800/600?r=2024-11-16T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E7%94%A8%E7%8E%A9%E5%AE%A2%E4%BA%91%E6%90%AD%E5%BB%BAwaline%E7%95%99%E8%A8%80%E6%9D%BF/","title":"用玩客云搭建waline留言板"},{"content":"前言 最近发现了一个非常有趣的开源项目：Bash-Games。这个项目将经典的小游戏移植到了 Shell 脚本中，使用纯文本界面（TUI）运行，非常适合那些热爱命令行、喜欢挑战和怀旧的玩家们。\n试玩 克隆项目： git clone https://github.com/liungkejin/Bash-Games.git 进入项目目录并运行游戏。例如，运行俄罗斯方块游戏： cd Bash-Games ./tetris.sh 游戏通过vim操作方式，即：上（J）、下（K）、左（H）、右（L）操控，单手就能玩！ 结语 当然游戏当中我还是发现几处明显的bug，对有兴趣的朋友自己去探索发现修改吧。\n","date":"2024-11-16T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-11-16","permalink":"https://zfj1441.com/p/%E8%BF%99%E9%A1%B9%E7%9B%AE%E5%A4%AA%E7%89%9B%E4%BA%86%E7%94%A8%E7%BA%AFbash%E5%AE%9E%E7%8E%B0%E7%BB%8F%E5%85%B8%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97%E8%B4%AA%E5%90%83%E8%9B%87%E6%B8%B8%E6%88%8F/","title":"这项目太牛了，用纯Bash实现经典俄罗斯方块、贪吃蛇游戏"},{"content":"前言 因腾讯云服务器快到期，准备用闲置玩客云安+cloudflare转发方案进行替换。但经过2个多月的试用，博客系统访问体验不尽人意。之后有多位好友推荐使用cloudflare tunnel隧道做内网穿透方案，试用一周感觉确实比cloudflare转发速度上块不少，特来分享启动方式。\nTunnel可以做什么 通过Public Hostname模式，将本地网络的服务暴露到公网，实现内网穿透。 例如我们在本地服务器 192.168.1.1:3000 搭建了一个 Transmission 服务用于 BT 下载，我们只能在内网环境才能访问这个服务，但通过内网穿透技术，我们可以在任何广域网环境下访问该服务。相比 NPS 之类传统穿透服务，Tunnel 不需要公网云服务器，同时自带域名解析，无需 DDNS 和公网 IP。 通过Private Network模式，实现异地组网。 例如，南昌有台电脑A，上海有台电脑B，两台电脑都加入Tunnel组成一个超大私人“局域网”，局域网内电脑可以相互访问，比如实现windows远程、ssh、智能家居控制等。 将非常规端口服务转发到 80/443 常规端口。 无论是使用公网 IP + DDNS 还是传统内网穿透服务，都免不了使用非常规端口进行访问，如果某些服务使用了复杂的重定向可能会导致 URL 中端口号丢失而引起不可控的问题，同时也不够优雅。 自动为你的域名提供 HTTPS 认证。 为你的服务提供额外保护认证。 最重要的是——免费。 开启Tunnel隧道 这里通过Public Hostname模式将局域网内的博客系统暴露到公网上，简要开启步骤：\n在cloudflare绑定自己的域名（如果已绑定请删除dns记录、转发规则、重定向规则，否则后续添加配置域名和转发URL会报错） 开启Zero Trust。选择绑定的域名-\u0026gt;Access -\u0026gt; zero trust（可免绑定支付方式） 创建隧道并在服务器端安装cloudflare程序。（推荐到 cloudflare github下载程序，因github上提供的平台程序更多，玩客云程序官网就没有而github上有下载） 配置域名和转发URL（这里需要注意内网服务器一定是http服务，如果是https服务会报502的错误，具体原因不明）\n更详细的设置可参考： Cloudflare Tunnel试用 总结 通过cloudflare Tunnel方式做内网穿透把内网服务暴露到公网，博客系统访问速度相比cloudflare转发功能速度快上很多，经过测试最高有250kb/s左右（肉眼测速，不接受反驳）。虽然无法把家庭带宽拉满，但基本网页访问和远程还是挺不错的。另外就是安全问题，1）请开启cloudfalre账号二次验证功能 2)妥善保存Tunnel配置中的token。下次琢磨着搞个三层交换机把玩客云从内网隔离开，防止玩客云沦陷后内网其他设备遭殃。\n参考 Cloudflare Tunnel试用\nCloudFlare Tunnel 免费内网穿透的简明教程\n","date":"2024-11-09T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-11-09","permalink":"https://zfj1441.com/p/cloudflare-tunnel%E5%85%8D%E8%B4%B9%E9%9A%A7%E9%81%93%E4%BD%93%E9%AA%8C/","title":"cloudflare tunnel免费隧道体验"},{"content":"前言 最新在公司内网装几台麒麟V10服务器，到选系统类型时候犯了迷糊不知道选哪种，最后选了带桌面的基础版服务器。结果发现一些常用命令：telnet、ftp、ntp等都没有安装。配置好ip地址想测试下网络通不通都束手无策。最后是几经折腾总行搞定，在这分享如何通过系统盘或ISO镜像包安装程序。\n正文 挂载系统镜像 #如果是光盘 mount /dev/cdrom /mnt # 如果是iso系统镜像 mount /opt/filename.iso /mnt 修改软件源 #添加源配置 vi /etc/yum.repos.d/local.repo [local_server] name=local_repo baseurl=file:///mnt enabled=1 gpgcheck=0 #0-不校验gpgkey公钥, 1-启动校验gpgkey公钥 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-kylin 软件安装 # 清除yum缓存 yum clean all # 重新创建yum缓存 yum makecache # 查找待安装的软件 如:lftp yum search lftp # 安装软件 yum install lftp -y 收尾工作 # 卸载光盘或ios镜像 umount /mnt # 拔光驱走人 总结 好记性不如烂笔头，来回经历过几次，写个博客记录下。下次碰到这种事应该不用再百度搜索了。\n","date":"2024-10-31T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-10-31","permalink":"https://zfj1441.com/p/%E9%BA%92%E9%BA%9Fv10%E7%B3%BB%E7%BB%9F%E9%80%9A%E8%BF%87%E5%85%89%E7%9B%98%E6%88%96iso%E9%95%9C%E5%83%8F%E5%8C%85%E7%A6%BB%E7%BA%BF%E5%AE%89%E8%A3%85%E7%A8%8B%E5%BA%8F/","title":"麒麟v10系统通过光盘或iso镜像包离线安装程序"},{"content":"前言 最近在github上发现一个牛逼的项目jaywcjlove/linux-command（项目搜集了 580 多个 Linux 命令，是一个非盈利性的仓库，生成了一个 web 网站方便使用，目前网站没有任何广告，内容包含 Linux 命令手册、详解、学习，内容来自网络和网友的补充，非常值得收藏的 Linux 命令速查手册），当时觉得非常有意思且有实际使用场景，所以用docker复刻一个自己的linux-command服务https://zfj1441.com/linux。之后又看到有人发布成了小程序，觉得这是个不错的主意。结合自己制作的”二维码名片“、”节日头像库“，再搞个“命令行宝典\u0026quot;，组成小程序工具链。\n正文 经过几个晚上的初步设计和技术探查，结果都不理想。无奈又上github上搜索，果不其然发现Zming-ming/linux-command项目，大佬实现了linux-command微信小程序版且代码全部开源。clone下代码用“微信开发者工具打开\u0026quot;，功能完整微信小程序就展现在你眼前。\n之后就是微信小程序注册、代码上传、版本发布审核、logo设计、小程序备案、小程序认证。我的“命令行宝典\u0026quot;就这样轻轻松松上线了。\n总结 github就是程序员的天堂，有啥不会搜一搜就有志同道合的人。再次感谢jaywcjlove/linux-command、Zming-ming/linux-command大佬的开源项目付出。下一步工作：1）同步更新linux-command命令 2) 优化小程序中bug 3）增加bug异常反馈、热门命令等功能\n","date":"2024-10-28T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-10-28","permalink":"https://zfj1441.com/p/%E5%8F%91%E5%B8%83%E4%B8%8A%E7%BA%BF%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%AE%9D%E5%85%B8%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F/","title":"发布上线\"命令行宝典\"微信小程序"},{"content":"前言 几年前就有想装黑苹果的想法，但奈何技术不行尝试几次都以失败告终，最后直接入手款macmini2014。但是随着系统、软件不断迭代升级感觉这macmini快不行，打开应用软件反应迟钝。所以琢磨着给macmini升级或替换，各种论坛网站公众号瞎逛后看中华硕chromebox3，二手价格600左右，设备最大特点是原厂配件就可以安装黑苹果且黑苹果安装教程非常详细。上周到手chromebox3卖家贴心的刷好bois和win10系统，趁着周末开刷黑苹果。\n黑苹果安装 整个刷机过程都是按着国光大佬的教程，一步步做的。\n教程需要注意两点：\n刷机时用国光大佬第一版efi，系统做完后用第二版efi启动(用第二版刷机会卡在8苹果处)。 chromebox3自带intel无线蓝牙驱动安装。国光大佬为完美黑苹果将原intel无线网卡更换成免驱bcm无线网卡所以教程没有详细说明原厂intel无线网卡wifi和蓝牙驱动安装方式，我这参考其他大佬教程安装。 原版intel无线网卡和蓝牙安装：\n哦 没有原版的 intel 无线网卡对应的驱动，参考\nhttps://blog.csdn.net/weixin_45518621/article/details/127606193\n下载三个 release，然后把其中四个拖入到kexts 文件夹里，然后拖拽到kernel 内核设置里面就好了；多余驱动我没删，目前没啥问题。(lilu.kext要放在前面 有依赖关系 不过 oc configurator会报错提示)\n多谢大佬的基础设置。\nBlueToolFixup\nIntelBTPatcher\nIntelBluetoothFirmware\nAirportItlwm\nhttps://github.com/OpenIntelWireless/itlwm/releases\nhttps://github.com/acidanthera/BrcmPatchRAM/releases\nhttps://github.com/OpenIntelWireless/IntelBluetoothFirmware/releases\n总结 和老旧macmini2014相比chromebox3硬件好太多了，黑苹果应用响应速度提高很多，但因为wifi芯片问题，不能使用苹果生态功能，确实有点不习惯。先这么用段时间，后续看看后期是否有必要像大佬一样换免驱bcm无线网卡。\n参考 ChromeBox 小主机黑苹果安装及双系统引导教程\n黑苹果 Monterey wifi 蓝牙驱动 AX系网卡 解决方案\n","date":"2024-10-20T00:00:00Z","image":"https://picsum.photos/800/600?mom=302\u0026r=2024-10-20","permalink":"https://zfj1441.com/p/%E8%B7%9F%E7%9D%80%E5%A4%A7%E4%BD%AC%E6%95%99%E7%A8%8B%E4%B8%BAchromebox3%E5%AE%89%E8%A3%85%E9%BB%91%E8%8B%B9%E6%9E%9C/","title":"跟着大佬教程为chromebox3安装黑苹果"},{"content":"前言 家里自建的NAS已经用了6年有余。一开始只是自己尝试着照片备份整理，远程访问。后面慢慢的拉上家人一起做照片备份管理。再加上最近点4K电影，硬盘空间使用率突然就涨起来了。看着存储空间红色进度条，折腾的心思又开始作祟。\n正文 目前机器上三块硬盘，分别是2个1TB + 1个1.8TB组raid5，实际使用空间是1.8TB。本着省钱给娃买奶粉的初衷，最后在闲鱼花300大洋淘了2个2.7TB二手硬盘，据商家说是5000小时左右使用时长。但我是抱着怀疑的态度的，毕竟硬盘改数据太容易了，最后质量怎么样还得看人品。升级计划是用2.7TB硬盘替换掉1TB硬盘。最终是2个2.7TB + 1个1.8TB组raid5，可使用空间扩容到3.6TB。\n提醒：数据无价，操作前请做好备份！\n提醒：数据无价，操作前请做好备份！\n提醒：数据无价，操作前请做好备份！\n扩容步骤 扩容过程中群晖系统不需要关机，全程开机操作。新硬盘一块一块替换，前一个硬盘数据同步完成后，再替换第二块硬盘。 具体步骤：\n删除新硬盘的所有分区 在群晖系统中查看待替换硬盘号并在物理机上做好标记 群晖系统中停用待替换的旧硬盘。存储管理器-\u0026gt;HDD/SSD-\u0026gt;操作-\u0026gt;停用硬盘 拔出停用的旧硬盘并换上新硬盘（重要：请确保停用的硬盘和拔出的硬盘是同一个，若拔错raid5直接失效） 待群晖系统识别新硬盘后，在存储管理器中选择修复，选择新硬盘。然后静静等待数据同步（预计1-2天时间） 待数据同步完成后，重复3-5步骤替换第二块硬盘。第二次修复时系统会提示是否扩容存储空间，选择是。 总结 数据无价，请一定做好数据备份。别想着没什么重要数据备份不备份没关系，当你想要用的时候就会拍大腿后悔没备份（经历过几次了，都是血的教训）。这里引用星爷一句话：以前有份珍贵的数据摆在我眼前，我没有珍惜，直到失去的时候才后悔莫及。\n","date":"2024-10-15T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-10-15","permalink":"https://zfj1441.com/p/nas%E7%BE%A4%E6%99%96%E7%B3%BB%E7%BB%9F%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4%E6%89%A9%E5%AE%B9%E6%AD%A5%E9%AA%A4/","title":"NAS群晖系统，存储空间扩容步骤"},{"content":"前言 9月份我使用玩客云在家里搭建个人博客，当时使用的anatole主题。这个主题和stack相比使用的人比较少，配置修改资料也相当有限。在这分享下anatole主题接入waline，实现留言和访问量统计，供有需要的各位参考。\n正文 waline相关配置 vi config/_default/params.toml disableComments = false [comments] enabled = true provider = \u0026#34;waline\u0026#34; [comments.waline] serverURL = walineServer地址 lang = \u0026#34;zh-CN\u0026#34; search = false pageview = true comment = true reaction = [ \u0026#34;https://unpkg.com/@waline/emojis@1.1.0/weibo/weibo_heart_eyes.png\u0026#34;, \u0026#34;https://unpkg.com/@waline/emojis@1.1.0/weibo/weibo_dog_consider.png\u0026#34;, \u0026#34;https://unpkg.com/@waline/emojis@1.1.0/weibo/weibo_sob.png\u0026#34; ] emoji = [ \u0026#34;https://unpkg.com/@waline/emojis@1.0.1/weibo\u0026#34; ] requiredMeta = [ \u0026#34;name\u0026#34;, \u0026#34;email\u0026#34;, \u0026#34;url\u0026#34; ] placeholder = \u0026#34;欢迎留下宝贵的评论！\u0026#34; [comments.waline.locale] admin = \u0026#34;站长\u0026#34; sofa = \u0026#34;还没有人评论哦！快来抢沙发吧~\u0026#34; placeholder = \u0026#34;可免登录留言，欢迎留下宝贵的评论！请留下正确的邮箱以便有回复时进行邮箱提醒。\u0026#34; reactionTitle = \u0026#34;这篇文章对你有帮助吗？\u0026#34; reaction0 = \u0026#34;有帮助\u0026#34; reaction1 = \u0026#34;一般\u0026#34; reaction2 = \u0026#34;看不懂\u0026#34; mkdir -p layouts/partials/comments/ vi layouts/partials/comments/waline.html \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://unpkg.com/@waline/client@v3/dist/waline.css\u0026#34;/\u0026gt; \u0026lt;div id=\u0026#34;waline\u0026#34; class=\u0026#34;waline-container\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;style\u0026gt; .waline-container { background-color: var(--card-background); border-radius: var(--card-border-radius); box-shadow: var(--shadow-l1); padding: var(--card-padding); --waline-font-size: var(--article-font-size); } .waline-container .wl-count { color: var(--card-text-color-main); } \u0026lt;/style\u0026gt; {{- $showReaction := (default true .Params.reaction) -}} {{- with .Site.Params.comments.waline -}} {{- $config := dict \u0026#34;el\u0026#34; \u0026#34;#waline\u0026#34; \u0026#34;dark\u0026#34; `html[data-scheme=\u0026#34;dark\u0026#34;]` -}} {{- $replaceKeys := dict \u0026#34;serverurl\u0026#34; \u0026#34;serverURL\u0026#34; \u0026#34;requiredmeta\u0026#34; \u0026#34;requiredMeta\u0026#34; \u0026#34;wordlimit\u0026#34; \u0026#34;wordLimit\u0026#34; \u0026#34;pagesize\u0026#34; \u0026#34;pageSize\u0026#34; \u0026#34;imageuploader\u0026#34; \u0026#34;imageUploader\u0026#34; \u0026#34;texrenderer\u0026#34; \u0026#34;texRenderer\u0026#34; \u0026#34;commentsorting\u0026#34; \u0026#34;commentSorting\u0026#34; \u0026#34;recaptchav3key\u0026#34; \u0026#34;recaptchaV3Key\u0026#34; \u0026#34;turnstilekey\u0026#34; \u0026#34;turnstileKey\u0026#34; -}} {{- $replaceLocaleKeys := dict \u0026#34;reactiontitle\u0026#34; \u0026#34;reactionTitle\u0026#34; \u0026#34;gifsearchplaceholder\u0026#34; \u0026#34;gifSearchPlaceholder\u0026#34; \u0026#34;nickerror\u0026#34; \u0026#34;nickError\u0026#34; \u0026#34;mailerror\u0026#34; \u0026#34;mailError\u0026#34; \u0026#34;wordhint\u0026#34; \u0026#34;wordHint\u0026#34; \u0026#34;cancellike\u0026#34; \u0026#34;cancelLike\u0026#34; \u0026#34;cancelreply\u0026#34; \u0026#34;cancelReply\u0026#34; \u0026#34;uploadimage\u0026#34; \u0026#34;uploadImage\u0026#34; -}} {{- range $key, $val := . -}} {{- if ne $val nil -}} {{- $replaceKey := index $replaceKeys $key -}} {{- $k := default $key $replaceKey -}} {{- if eq $k \u0026#34;locale\u0026#34; -}} {{- $locale := dict -}} {{- range $lkey, $lval := $val -}} {{- if ne $lval nil -}} {{- $replaceLKey := index $replaceLocaleKeys $lkey -}} {{- $lk := default $lkey $replaceLKey -}} {{- $locale = merge $locale (dict $lk $lval) -}} {{- end -}} {{- end -}} {{- $config = merge $config (dict $k $locale) -}} {{- else if eq $k \u0026#34;reaction\u0026#34; -}} {{- $config = merge $config (dict $k (cond $showReaction $val false)) -}} {{- else -}} {{- $config = merge $config (dict $k $val) -}} {{- end -}} {{- end -}} {{- end -}} \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import { init } from \u0026#39;https://unpkg.com/@waline/client@v3/dist/waline.js\u0026#39;; init({{ $config | jsonify | safeJS }}); \u0026lt;/script\u0026gt; {{- end -}} 文章中增加留言 和 访问量展示 mkdir -p layouts/_default cp theme/anatole/layouts/_default/single.html layouts/_default/ vi layouts/_default/single.html {{ if or (eq .Type \u0026#34;post\u0026#34;) (eq .Type .Site.Params.postSectionName) }} \u0026lt;ul class=\u0026#34;post__meta\u0026#34;\u0026gt; \u0026lt;li class=\u0026#34;post__meta-item\u0026#34;\u0026gt; \u0026lt;em class=\u0026#34;fas fa-calendar-day post__meta-icon\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;post__meta-text\u0026#34; \u0026gt;{{ if isset .Site.Params \u0026#34;singledateformat\u0026#34; }} {{ if .Site.Params.localizedDates }} {{ time.Format .Site.Params.singleDateFormat .Date }} {{ else }} {{ .Date.Format .Site.Params.singleDateFormat }} {{ end }} {{ else }} {{ if .Site.Params.localizedDates }} {{ time.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; .Date }} {{ else }} {{ .Date.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; }} {{ end }} {{ end }} \u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;li class=\u0026#34;post__meta-item\u0026#34;\u0026gt; \u0026lt;em class=\u0026#34;fas fa-stopwatch post__meta-icon\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;post__meta-text\u0026#34;\u0026gt;约{{ .ReadingTime }} 分钟\u0026lt;/span\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;li class=\u0026#34;post__meta-item\u0026#34;\u0026gt; \u0026lt;em class=\u0026#34;fas fa-stopwatch post__meta-icon\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;waline-pageview-count\u0026#34; data-path=\u0026#34;{{.RelPermalink}}\u0026#34;\u0026gt;0\u0026lt;/span\u0026gt; 次浏览 \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; {{ end }} {{- if .Site.Params.comments.enabled -}} \u0026lt;div id=\u0026#34;comment\u0026#34;\u0026gt; {{ partial \u0026#34;comments/waline.html\u0026#34; . }} \u0026lt;/div\u0026gt; {{- end -}} 首页增加访问量、评论量展示 mkdir -p layouts/ cp theme/anatole/layouts/index.html layouts/ vi layouts/index.html \u0026lt;div class=\u0026#34;post__footer\u0026#34;\u0026gt; \u0026lt;em class=\u0026#34;fas fa-calendar-day\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;post__footer-date\u0026#34; \u0026gt;{{ if isset .Site.Params \u0026#34;indexdateformat\u0026#34; }} {{ if .Site.Params.localizedDates }} {{ time.Format .Site.Params.indexDateFormat .Date }} {{ else }} {{ .Date.Format .Site.Params.indexDateFormat }} {{ end }} {{ else }} {{ if .Site.Params.localizedDates }} {{ time.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; .Date }} {{ else }} {{ .Date.Format \u0026#34;Mon, Jan 2, 2006\u0026#34; }} {{ end }} {{ end }}\u0026lt;/span\u0026gt; \u0026lt;em class=\u0026#34;fas fa-calendar-day\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;waline-pageview-count\u0026#34; data-path=\u0026#34;{{.RelPermalink}}\u0026#34;\u0026gt;0\u0026lt;/span\u0026gt; 次浏览 \u0026lt;em class=\u0026#34;fas fa-calendar-day\u0026#34;\u0026gt;\u0026lt;/em\u0026gt; \u0026lt;span class=\u0026#34;waline-comment-count\u0026#34; data-path=\u0026#34;{{.RelPermalink}}\u0026#34;\u0026gt;0\u0026lt;/span\u0026gt; 条评论 \u0026lt;br/\u0026gt; {{ with .Page.Params.Categories }} {{ partial \u0026#34;taxonomy/categories.html\u0026#34; . }} {{ end }} {{ with .Page.Params.Tags }} {{ partial \u0026#34;taxonomy/tags.html\u0026#34; . }} {{ end }} \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;pagination\u0026#34;\u0026gt; {{ template \u0026#34;partials/pagination.html\u0026#34; . }} \u0026lt;/div\u0026gt; \u0026lt;!-- waline3 start --\u0026gt; \u0026lt;script type=\u0026#34;module\u0026#34;\u0026gt; import { pageviewCount } from \u0026#39;https://unpkg.com/@waline/client@v3/dist/pageview.js\u0026#39;; import { commentCount } from \u0026#39;https://unpkg.com/@waline/client@v3/dist/comment.js\u0026#39;; commentCount({ serverURL: \u0026#34;{{.Site.Params.comments.waline.serverURL}}\u0026#34; }); pageviewCount({ serverURL: \u0026#34;{{.Site.Params.comments.waline.serverURL}}\u0026#34; }); \u0026lt;/script\u0026gt; \u0026lt;!-- waline3 end --\u0026gt; 总结 按以上步骤anatole就可以正常使用waline留言、访问量统计、评论统计功能。细心的你可能发现访问量和评论数量前的图片是错的，下次找时间再改改。\n参考 使博客更好地接入 Waline\n","date":"2024-10-13T23:41:59+08:00","image":"https://picsum.photos/800/600?r=2024-10-13T23:41:59+08:00","permalink":"https://zfj1441.com/p/anatole%E4%B8%BB%E9%A2%98%E5%8D%9A%E5%AE%A2%E6%8E%A5%E5%85%A5waline/","title":"anatole主题博客接入waline"},{"content":"前言 《大秦赋》豆瓣评分其实并不高，之所以选择这部剧，主要有三个原因：一是作为历史盲来说，追剧的同时又能了解历史，爽哉；二是翻看了其他秦国历史剧都比较老画质无法入眼；三是冲着演员，大部分演员在其他影视剧都所了解。\n内容概要 《大秦赋》是一部讲述秦国从战国末期到秦始皇统一六国的历史大剧。该剧从历史的角度再现了秦国的崛起、变革和统一的全过程。\n思考 剧情内容不好评价，毕竟拍成电视剧和实际历史应该有不少差距，但是对于初步了解秦国历史确实不错的选择，有机会再详读秦国历史\n","date":"2024-10-13T09:41:59+08:00","image":"https://picsum.photos/800/600?r=2024-10-13","permalink":"https://zfj1441.com/p/%E5%A4%A7%E7%A7%A6%E8%B5%8B%E8%A7%82%E5%90%8E%E6%84%9F/","title":"《大秦赋》观后感"},{"content":"前言 最近飓风影视谈B站视频画质与过度压缩问题闹得沸沸扬扬。看着网上激烈的讨论，我突然好奇一个，4K电影不做压缩到底占多大硬盘空间？从网上找了些资料，了解像素、图片、视频空间计算。在这记录下一部2小时4K30帧电影存储空间计算。\n正文 像素： •\t像素是构成数字图像和视频的最小单位。每个像素代表一个特定颜色的小方块，通过成千上万的像素组合，形成完整的图像或视频帧。 •\t每个像素通常包含三个颜色值（红色、绿色、蓝色，即RGB），这些颜色的组合可以显示出丰富的色彩。 图片： •\t图片是由像素组成的静态视觉表现。当你拍摄一张照片或查看数字图像时，实际上是在查看大量像素的排列。 •\t图片的分辨率（如1920x1080）表示图片的像素数，即宽度和高度方向上各自包含的像素数量。分辨率越高，图片越清晰，细节越多。 视频： •\t视频是由一系列连续的图片（称为帧）组成的动态影像。每一帧本质上是一张图片，包含若干个像素。 •\t视频的帧率（如每秒30帧，即FPS）表示每秒钟播放的帧数，帧率越高，视频的流畅度越好。视频的每一帧和静态图片一样，由像素构成，因此视频的分辨率也是基于每一帧的像素数量来定义的（如1080p、4K等）。 总结来说，像素是图片和视频的最基本组成单位，而图片是静态的像素排列，视频则是由一系列连续变化的图片组成的动态表现。\n一个像素 = 3字节（RGB，每个颜色需1字节）\n一张4K图片 = 3840 x 2160 = 8294400 像素\n一秒视频 = 30帧（每秒播放30张图片，人眼不会感觉卡顿）\n1分钟 = 60秒\n1小时 = 60分钟\n总字节数:\n3 * 3840 * 2160 * 30 * 60 * 60 * 2 = 5374771200000 字节\n转换下存储格式：\n5374771200000 / 1024 / 1024 / 1024 / 1024 ≈ 4.89 TB\n总结 这一算不得了，我这2T的机械一盘一部电影都放不下，这视频压缩确实非常有必要。\n参考 一个像素占多大内存 多少字节\n","date":"2024-10-11T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-10-11","permalink":"https://zfj1441.com/p/%E8%AE%BA%E8%A7%86%E9%A2%91%E5%8E%8B%E7%BC%A9%E7%9A%84%E9%87%8D%E8%A6%81%E6%80%A7/","title":"论视频压缩的重要性"},{"content":"前言 自《闲置玩客云设备利用组个负载均衡》文章中使用视频作为演示负载均衡效果，我在浏览过程中发现视频虽然只有4M大小，但因为服务带宽有限视频加载非常慢。所以有了优化网站访问体验的想法，今天在浏览其他大佬博客时发现他们图片和视频大多是采用webp格式。了解下webp优势后，准备把全站图片资源转成webp，以下是我博客图片转换实战，仅供参考。\nWebP是一种现代图片格式，旨在为网络上的图片提供出色的无损和有损压缩。WebP格式由Google开发，派生自VP8图像编码格式，支持有损和无损压缩。\nWebP格式具有以下特点：\n压缩效率高：WebP格式可以在保持相同图像质量的情况下，将文件大小显著减小。例如，WebP格式的文件通常比JPEG文件小约30%。 支持无损和有损压缩：WebP支持两种压缩方式，无损压缩适用于需要完全保留原始图像细节的场景，而有损压缩则适用于可以接受一定图像质量损失以换取更小文件大小的场景。 硬件加速：WebP格式支持硬件加速解码，可以提高图片加载速度。 开源：WebP格式是开源的，这意味着它可以被广泛应用于不同的平台和设备上。 WebP格式的优势包括：\n提高网页加载速度：由于文件大小显著减小，使用WebP格式的图片可以显著提高网页的加载速度，提升用户体验。 节省带宽：较小的文件大小意味着可以减少数据传输量，从而节省带宽资源。 兼容性好：现代浏览器如Chrome、Firefox、eged、safair等都已经支持WebP格式，使得这种格式在实际应用中具有很好的兼容性。 批量操作难免会误伤，请提前做好版本控制!\n批量操作难免会误伤，请提前做好版本控制!\n批量操作难免会误伤，请提前做好版本控制!\n开整 我博客内容资源目录结构，本次目标是将post目录下的文章资源全部改用webp格式。涉及到两步重要操作：\n批量转换png、jpg、jpeg转webp格式 批量修改文章（index.md），将引用的图片demo.png改成demo.webp ├── content │ └── post │ ├── 关于OpenSSH漏洞(CVE-2024-6387)修复 │ │ ├── IMG_0243.jpeg │ │ └── index.md │ └── 获取《王者荣耀》全英雄高清无码图 │ ├── 20180607141717128.png │ ├── 2018060714172921https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/1.png │ ├── 20180607144357253.png │ └── index.md ├── static │ └── img │ ├── 10.jpg │ ├── 11.jpg │ └── 12.jpg 检查图片资源 find . -name \u0026#34;*.png\u0026#34; | wc -l find . -name \u0026#34;*.jp*g\u0026#34; | wc -l 将图片转成webp格式 #将png、jpg、jpeg转成webp格式 find . -name \u0026#34;*.png\u0026#34; \u0026gt;\u0026gt; a.sh find . -name \u0026#34;*.jpg\u0026#34; \u0026gt;\u0026gt; a.sh find . -name \u0026#34;*.jpeg\u0026#34; \u0026gt;\u0026gt; a.sh #组织命令ffmpeg -i ./aaa/bbb/aa.png ./aaa./bbb/aa.webp vi a.sh :%s/^\\(.*\\).png/ffmpeg -i \\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/1.png \\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/1.webp/g :%s/^\\(.*\\).jpg/ffmpeg -i \\1.jpg \\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/1.webp/g :%s/^\\(.*\\).jpeg/ffmpeg -i \\1.jpeg \\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/1.webp/g bash ./a.sh 修改文章中的图片名称 #替换博客内的图片 find . -name \u0026#34;*.png\u0026#34; \u0026gt;\u0026gt; b.sh find . -name \u0026#34;*.jpg\u0026#34; \u0026gt;\u0026gt; b.sh find . -name \u0026#34;*.jpeg\u0026#34; \u0026gt;\u0026gt; b.sh #组织sed -i \u0026#34;s/aa.png/aa.webp\u0026#34; ./aaa/bbb/index.md命令 vi b.sh :%s/^\\(.*)\\/\\(.*\\).png/sed -i \u0026#34;s/\\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/2.png\\/\\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/2.webp\\/g\u0026#34; \\1\\/index.md/g :%s/^\\(.*)\\/\\(.*\\).jpg/sed -i \u0026#34;s/\\2.jpg\\/\\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/2.webp\\/g\u0026#34; \\1\\/index.md/g :%s/^\\(.*)\\/\\(.*\\).jpeg/sed -i \u0026#34;s/\\2.jpeg\\/\\https://image.vitshare.cn/blog/content/post/博客文章图片批量转webp格式实战/2.webp\\/g\u0026#34; \\1\\/index.md/g #此步骤会因路径带有/或(等特殊字符导致sed命令执行失败 #将报错行注释,后续手工处理 bash ./b.sh #监测修改内容是否正确替换 git diff -- \u0026#39;*.md\u0026#39; #部署后逐一浏览文章检查是否有图片异常 转换后的效果 总结 因本地各种缓存，图片转换后对博客体验提升多少不好测算。但是图片大小降低了至少10倍，从网络传输角度看体验应该有很大提升。而且视频mov、mp4也可以转换成webm格式，体验会更进一步提升。\n参考 WebP\\WebM新媒体格式的网站优化\n","date":"2024-09-28T00:00:00Z","image":"https://picsum.photos/800/600?r=20240928","permalink":"https://zfj1441.com/p/%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0%E5%9B%BE%E7%89%87%E6%89%B9%E9%87%8F%E8%BD%ACwebp%E6%A0%BC%E5%BC%8F%E5%AE%9E%E6%88%98/","title":"博客文章图片批量转webp格式实战"},{"content":"前言 中秋假期带娃去弋阳龟峰一日游，上午10点入景区下午4点才踏上回家的路，主打就是花钱爬山锻炼身体。晚上整理照片，全程6小时拍了一百多张照片。但是来来回回翻了几遍，最后只挑出3张看的比较顺眼的。我这拍照技术\u0026hellip;\u0026hellip;。马上十一假期出游是肯定的，所以提前学下如何通过照片构图快速提升拍照水平。\n设备和现有水平\n设备： iphone12（每当人在景区时才想到我有相机）\n目标： 自己看的顺眼\n技术水平： 无（用相机也只会对焦然后咔嚓）\n9中拍照常用的构图 居中构图 三分法构图 对角线构图 对称构图 框架构图 三角形构图 留白构图 引导构图 前景构图 总结 带着以上构图的思路去翻了翻NAS中照片，感觉还有那么回事。在什么都 不懂的情况下用的最多的就是居中构图、偶尔碰巧会用上三分构图、引导线构图。\n参考 今天给大家整理了9种摄影构图 一看就会 九种摄影构图法 拍摄必备13中构图方法\n","date":"2024-09-25T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-25","permalink":"https://zfj1441.com/p/%E5%81%87%E6%9C%9F%E5%87%BA%E6%B8%B8%E5%89%8D%E6%81%B6%E8%A1%A5%E4%B8%8B%E6%8B%8D%E7%85%A7%E6%8A%80%E5%B7%A7/","title":"假期出游前，恶补下拍照技巧"},{"content":"前言 上一篇《利用cloudflare转发实现域名免端口访问内网服务》我们通过cloudflare代理转发功能实现域名免端口方式访问家庭内网的博客系统，基本上已经可以完美替代云服务器上的博客系统。本次按计划将剩余几台玩客云组个负载均衡，以提高博客系统对外服务性能（其实也没啥访问量，纯属闲置设备利用）。\n开整 这里用到三台设备，1台作为负载均衡服务（服务器A：192.168.1.80），两台作为子节点提供博客系统（服务器B：192.168.1.81，服务器C：192.168.1.82）。 服务器A，提供https代理 和 负载均衡，且需要有IPv6地址可对外提供服务。 服务器B、服务器C 提供博客系统，内网http服务即可\n服务器A配置 vi /etc/nginx/nginx.conf http{ ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE ssl_prefer_server_ciphers on; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # 定义backend服务器集群 upstream backend { server 192.168.1.81; # 服务器B server 192.168.1.82; # 服务器C } # 主服务器提供https服务 server { listen 8099 ssl default_server; listen [::]:8099 ssl default_server; ssl_certificate /home/zfj/etc/zfj1441.eu.org.pem; ssl_certificate_key /home/zfj/etc/zfj1441.eu.org.key; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } } # 重启nginx systemctl restart nginx 服务器B/C配置 vi /etc/nginx/sites-enabled/default # 子服务器只需要提供http服务 server{ server_name _; location / { root /home/zfj/www/site/public; index index.html index.htm index.nginx-debian.html; try_files $uri $uri/ =404; } } # 重启nginx systemctl restart nginx 其他基本配置 路由器防火墙放通服务器A的https服务 cloudflare域名DNS解析到服务器A的IPv6地址，为方便验证开启“开发模式”（不实用cdn缓存) 测试验证 为方便观察https请求到哪台服务器上，分别修改服务器B、服务器C博客系统主页内容 Your browser doesn't support HTML5 video. Here is a link to the video instead. 总结 本次只演示了基本的负载均衡能力，服务器B、服务器C轮询被服务器A调用。复杂的负载均衡以后有需要在研究。\n","date":"2024-09-22T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-22","permalink":"https://zfj1441.com/p/%E9%97%B2%E7%BD%AE%E7%8E%A9%E5%AE%A2%E4%BA%91%E8%AE%BE%E5%A4%87%E5%88%A9%E7%94%A8%E7%BB%84%E4%B8%AA%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1/","title":"闲置玩客云设备利用，组个负载均衡"},{"content":"前言 上一篇《利用域名访问玩客云博客系统》通过域名解决了IPV6使用不便和IPv6地址随机变动问题，最终我们使用域名+端口方式成功访问家庭内网博客系统。如何进一步实现通过域名直接访问家庭内网的服务呢？这里就说下家庭网络服务商（联通、电信、移动、长城等）都会封闭常用服务端口已保证内网安全，常见被封的端口有：80、443、21、22、3389等。这里分享我如何通过cloudflare平台突破运营商封禁的端口，实现域名免端口访问内网服务。\n操作 开启DNS代理和端口转发 开启SSL并下载证书部署 # 使用root 编辑nginx配置 vi /etc/nginx/sites-enabled/default server { listen 8099 ssl default_server; listen [::]:8099 ssl default_server; ssl_certificate /home/zfj/etc/vitshare.cn.pem; ssl_certificate_key /home/zfj/etc/vitshare.cn.key; server_name _; location / { root /home/zfj/www/site/public; index index.html index.htm index.nginx-debian.html; try_files $uri $uri/ =404; } } #重启nignx服务 systemctl restart nginx.service 其他一些配置 设置完成后等待10分钟左右，就可以通过域名直接访问服务器内网了。 总结 通过cloudflare代理+端口转发功能突破运营商封锁端口，实现域名免加端口直接访问家庭内网服务。试用半个月cloudflare体验确实不错。功能上监控访问量、DDOS防御、CDN缓存、隐藏真实IP、以及IPv4和IPv6兼容等，最最重要的是这些都完全免费。至于代理后的访问速度，免费的东西就不要太强求。\n","date":"2024-09-19T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-19","permalink":"https://zfj1441.com/p/%E5%88%A9%E7%94%A8cloudflare%E8%BD%AC%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%9F%9F%E5%90%8D%E5%85%8D%E7%AB%AF%E5%8F%A3%E8%AE%BF%E9%97%AE%E5%86%85%E7%BD%91%E6%9C%8D%E5%8A%A1/","title":"利用cloudflare转发实现域名免端口访问内网服务"},{"content":"前言 上一篇《利用闲置玩客云部署hugo静态博客系统》我们已经实现内网访问博客系统。那么如果实现互联网访问呢？这个方案由很多，比如：公网IPv4、公网IPv6、内网穿透、跨区域组网等等。除公网IPv4以外，我建议使用公网IPv6，这种方式配置和使用过程比较简单易，没啥技术门槛。具体配置参考《设置光猫路由器开启IPv6》、《玩客云armbian系统固定ipv6后半段地址》，今天继续分享我是如何通过域名访问访问家里的玩客云博客系统。\n听我絮絮叨叨 通过IPv6地址+端口，我们已经可以实现随处访问，但会遇到两个麻烦事：\n1）玩客云或电脑的IPv6经常变动，比如：重启路由器；运营商每周或每月会重新下发新地址（强制变更）等等。\n2）IPv6地址128位，由数字+字母组成，使用时非常不方便。 那如何解决这些问题？答案就是：域名\n域名的作用主要包括：\n方便记忆与访问：域名使人们无需记住复杂的IP地址，通过直观、易于记忆的字符串来访问网站，提高了用户体验和网站的可达性。 品牌宣传：一个好的域名可以加强品牌形象，通过选择与品牌相关的域名，可以提高用户对品牌的认知和信任度。 搜索引擎优化(SEO)：合适的域名有助于提高网站在搜索结果中的排名，因为域名中的关键词可以增加网站在搜索引擎中的曝光度。 提升用户体验：域名可以直接反映网站的性质和内容，帮助用户准确地找到所需信息。 方便记忆与重复访问：简短、易拼写的域名提高了用户对网站的记忆度和重复访问率。 增加网络可信度：拥有一个独特且合适的域名能够增加网站的可信度，用户更倾向于信任那些拥有专业、合乎逻辑的域名的网站。 配置域名访问 申请域名 这里推荐一个永久免费的二级域名nic.eu.org 申请教程可参考《教你白嫖一个终身免费的顶级域名和设置解析的方法》 域名审批周期一般在5-30天，我大概等了2周才收到邮件提醒。\n绑定域名解析 注册并登陆cloudflare ，添加域名和dns解析记录\n等上十分钟左右就可以验证域名+端口访问玩客云个人博客\n最后通过cloudflare Api + 定时任务实现，自动更新dns解析记录 # 在玩客云中添加定时任务 crontab -e # 每1小时执行一次 2 */1 * * * python3 ~/bin/ddns.py \u0026gt;\u0026gt; ~/log/ddns.log \u0026#39;\u0026#39;\u0026#39; ddns.py 更新cloudflare dns域名解析记录 \u0026#39;\u0026#39;\u0026#39; import http.client import json import socket import netifaces import logging import os home_path = os.path.expanduser(\u0026#39;~\u0026#39;) logging.basicConfig(level=logging.DEBUG, format=\u0026#39;%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s\u0026#39;, datefmt=\u0026#39;%a, %d %b %Y %H:%M:%S\u0026#39;, filename=os.path.join(home_path, \u0026#39;log\u0026#39;, \u0026#39;ddns.log\u0026#39;), filemode=\u0026#39;a\u0026#39; ) AuthEmail = cloudflare账号邮箱地址 AuthKey = 我的个人资料-\u0026gt;api令牌-\u0026gt;Global API Key ZoneId = 网站-\u0026gt;概述-\u0026gt;区域ID(页面右下角) def getDnsList(): \u0026#39;\u0026#39;\u0026#39;获取已添加的dns记录\u0026#39;\u0026#39;\u0026#39; conn = http.client.HTTPSConnection(\u0026#34;api.cloudflare.com\u0026#34;) headers = { \u0026#39;Content-Type\u0026#39;: \u0026#34;application/json\u0026#34;, \u0026#39;X-Auth-Email\u0026#39;: AuthEmail, \u0026#39;X-Auth-Key\u0026#39;: AuthKey, } conn.request(\u0026#34;GET\u0026#34;, \u0026#34;/client/v4/zones/\u0026#34; + ZoneId + \u0026#34;/dns_records\u0026#34;, headers=headers) res = conn.getresponse() data = res.read() dataJson = json.loads(data.decode(\u0026#34;utf-8\u0026#34;)) if dataJson[\u0026#39;success\u0026#39;] == True: return dataJson[\u0026#39;result\u0026#39;] else: return [] def updateDns(recordId, name, ipv6): \u0026#39;\u0026#39;\u0026#39;更新dns记录\u0026#39;\u0026#39;\u0026#39; conn = http.client.HTTPSConnection(\u0026#34;api.cloudflare.com\u0026#34;) payload = { \u0026#34;comment\u0026#34;: \u0026#34;Domain verification record\u0026#34;, \u0026#34;name\u0026#34;: name, \u0026#34;proxied\u0026#34;: True, \u0026#34;tags\u0026#34;: [], \u0026#34;ttl\u0026#34;: 3600, \u0026#34;content\u0026#34;: str(ipv6), \u0026#34;type\u0026#34;: \u0026#34;AAAA\u0026#34; } headers = { \u0026#39;Content-Type\u0026#39;: \u0026#34;application/json\u0026#34;, \u0026#39;X-Auth-Email\u0026#39;: AuthEmail, \u0026#39;X-Auth-Key\u0026#39;: AuthKey } conn.request(\u0026#34;PUT\u0026#34;, \u0026#34;/client/v4/zones/\u0026#34;+ZoneId+\u0026#34;/dns_records/\u0026#34; + recordId, json.dumps(payload), headers) res = conn.getresponse() data = res.read() dataJson = json.loads(data.decode(\u0026#34;utf-8\u0026#34;)) return dataJson[\u0026#39;success\u0026#39;] def getLocalIPv6(): \u0026#39;\u0026#39;\u0026#39;获取本机有效ipv6地址\u0026#39;\u0026#39;\u0026#39; interfaces = netifaces.interfaces() ipv6_addr = None for interface in interfaces: if netifaces.AF_INET6 in netifaces.ifaddresses(interface): ipv6_addr = netifaces.ifaddresses(interface)[netifaces.AF_INET6][0][\u0026#39;addr\u0026#39;] if str(ipv6_addr).startswith(\u0026#39;2408\u0026#39;): break return ipv6_addr if __name__==\u0026#39;__main__\u0026#39;: ipv6 = getLocalIPv6() dnslist = getDnsList() for dns in dnslist: if dns[\u0026#39;content\u0026#39;] != ipv6: logging.info(\u0026#39;ip变化,新ip[\u0026#39; + ipv6 +\u0026#39;]\u0026#39;) if updateDns(dns[\u0026#39;id\u0026#39;], dns[\u0026#39;name\u0026#39;], ipv6): logging.info(\u0026#39;更新成功\u0026#39;) else: logging.info(\u0026#39;更新失败\u0026#39;) else: logging.info(\u0026#39;ip未变动\u0026#39;) 总结 通过cloudflare的域名解析可以很轻松访问，玩客云个人博客系统。且cloudflare提供IPv4、IPv6兼容，就算客户端是IPv4地址也同样可以访问玩客云的IPv6服务。下篇在分享通过cloudflare转发功能实现域名免端口访问玩客云博客系统（放在一篇讲内容是在太多），我自己都不愿理看更别说各位看官了。\n","date":"2024-09-12T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-12T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%88%A9%E7%94%A8%E5%9F%9F%E5%90%8D%E8%AE%BF%E9%97%AE%E7%8E%A9%E5%AE%A2%E4%BA%91%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F/","title":"利用域名访问玩客云博客系统"},{"content":"前言 事情的起因是：我在使用玩客云搭建博客过程中，想通设置路由器防火墙策略实现ipv6访问。在设置过程发现玩客云的ipv6地址后半段不是网卡MAC地址，这让我怀疑ipv6地址是随机变动。经重启路由器、玩客云确定，每次ipv6地址变化。玩客云ipv6地址前半段、后半段都变了。在我了解到的网络知识中ipv6前半段由运营商分配，后半段是设备MAC地址。所以这更定是哪里有问题，几经周折最后也是搞定，最后记录下。\n经过 了解ipv6获取及生产方式 《IPv6基础知识-地址配置方式》\n《 IPV6地址、IPV6单播地址》\n《IPv6 地址自动分配方式的Stateless(无状态)与Stateful(有状态)》\n经过大量信息参考和对ipv6基础知识阅读，确定只要把ipv6地址生成方式改成eui64就行。\narmbian设置ipv6生成规则 《Linux_ipv6_无状态_设置为_eui64_有状态ipv6更改后缀》 《随身wifi刷Debian系统，怎么固定ipv6后缀》\n《OpenWRT 22.03 固件下IPv6 防火墙与DHCP设置》\n《如何让客户端电脑的IPv6后半部分固定下来，而不是每次重启电脑都变化？》\n通过以上内容发现，已经有非常多的网友也遇到相同的问题，且和我有一样的想法：固定ipv6后半段地址，配置安全策略。\n最后方案 固定网口的mac地址 通过教程固定mac地址，解决重启mac自动变更的问题\n#修改 /etc/network/interfaces 和 /etc/network/interfaces.default配置 #删除所有内容后添加以下内容 allow-hotplug eth0 no-auto-down eth0 iface eth0 inet dhcp hwaddress ether 9F:3B:55:CB:1E:28 pre-up ifconfig eth0 hw ether 9F:3B:55:CB:1E:28 wifi固定ipv6后半段方案 vi /etc/NetworkManager/system-connections/ vi *.nmconnection [ipv6] addr-gen-mode=eui64 重启玩客云，就可以发现ipv6后半段地址已经是mac地址。\n有线固定ipv6后半段地址方案 参考 《Linux_ipv6_无状态_设置为_eui64_有状态ipv6更改后缀》 最后一段 “Debian10 让 NetworkManager 管理有线网卡”\n#1.在NetworkManager.conf最后加入managed=true vi /etc/NetworkManager/NetworkManager.conf [ifupdown] managed=true #2.注释掉/etc/network/interfaces 中 `allow-hotplug eth0` 和 `iface eth0 inet dhcp`这两行 #3.重启玩客云 reboot 总结 以上问题能顺利解决都是依靠互联网大佬分享精神，感谢。\n","date":"2024-09-08T17:30:13+08:00","image":"https://picsum.photos/800/600?r=2024-09-08T17:37:26+08:00","permalink":"https://zfj1441.com/p/%E7%8E%A9%E5%AE%A2%E4%BA%91armbian%E7%B3%BB%E7%BB%9F%E5%9B%BA%E5%AE%9Aipv6%E5%90%8E%E5%8D%8A%E6%AE%B5%E5%9C%B0%E5%9D%80/","title":"玩客云armbian系统固定ipv6后半段地址"},{"content":"前言 18年花300元租的腾讯云服务器马上快到期了，最近看了眼续费价格，折扣价5年1800元（真是老用户不如狗）。5*360=1800，每天一元？腾讯云服务器配置1核1G内存1Mb带宽，性能非常有限。所以它承载的服务也很有限，主要是：博客系统、小程序后端服务、一些定时脚本、写点测试程序。想要维持这些服务，又不想当冤大头，所以琢磨着替换方案。正好家里有用闲置玩客云，配置也是1G跑静态页面的博客应该不成问题。\n以上内容解决方案估计要花5篇讲述\n玩客云hugo博客系统安装 玩客云网络相关配置，实现域名+端口访问博客 利用cloudflare实现域名免加端口访问博客 hugo博客留言板，访问量统计功能 来个负载均衡？（把闲置4台设备总得用起来） 玩客云hugo博客系统安装 必要的软件工具 sudo apt install nginx sudo apt install hugo nginx -v hugo version 博客系统安装配置 创建站点目录 cd ~ \u0026amp;\u0026amp; hugo new site mysite(站点名称) 安装主题 博客系统主题选择方面看个人喜好https://themes.gohugo.io 有点需要注意玩客云上hugo版本相对较低，所以主题上尽量选2022年左右发布的。我这里选择stack 2022年发布的v3.15.0版本。\n#下载并解压到themes目录 cd /tmp/ \u0026amp;\u0026amp; wget https://github.com/CaiJimmy/hugo-theme-stack/archive/refs/tags/v3.15.0.zip unzip v3.15.0.zip cp -r /tmp/hugo-theme-stack-3.15.0/ ~/mysite/themes/hugo-theme-stack 默认配置启动博客 cp -r ~/mysite/themes/content ~/mysite cp ~/mysite/themes/Hugo-theme-stack/config.yaml ~/mysite #编译静态页面 cd ~/mysite \u0026amp;\u0026amp; hugo 配置nginx #使用root用户编辑 sudo vi /etc/nginx/sites-enabled/default #注释server段原有内容并添加以下内容 server { # 接入cloudflare使用 # listen 8099 ssl default_server; # listen [::]:8099 ssl default_server; # ssl_certificate /home/zfj/etc/zfj1441.eu.org.pem; # ssl_certificate_key /home/zfj/etc/zfj1441.eu.org.key; listen 8099 default_server; listen [::]:8099 default_server; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. root /home/zfj/mysite/public; index index.html index.htm index.nginx-debian.html; try_files $uri $uri/ =404; } } #重启ngixn服务 sudo systemctl restart nginx .\n总结 此时访问 http://{玩客云ip}:8099 就可以成功访问玩客云上的博客系统，之后再根据梯子需要修改config.yaml相关配置就算搭建好自己的个人博客。发布内容方式可以惨好hugo官方教程。\n参考 HUGO Quick start\n使用hugo搭建个人博客\n","date":"2024-09-08T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-08T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%88%A9%E7%94%A8%E9%97%B2%E7%BD%AE%E7%8E%A9%E5%AE%A2%E4%BA%91%E9%83%A8%E7%BD%B2%E9%83%A8%E7%BD%B2hugo%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E7%B3%BB%E7%BB%9F/","title":"利用闲置玩客云部署部署hugo静态博客系统"},{"content":"前言 今天把吃灰多年的玩客云重新插上电接入网络，但奇怪的是路由器管理端迟迟看不到新设备接入。我确定这玩意在丢进抽屉前基本的网络和账号密码是配置好的，但就是找不到设备IP。其实这时候只要重启下路由器就可以看到新设备，但是因为我这网络中在跑PCDN重启会影响第二天的收益，所以想在不重启的情况下找到设备。因此就想出通过CTF网络嗅探思路找出这台设备。\n开干 嗅探流程：\n通过ping命令监测内网存活的IP列表 人工对比路由器管理端可查询到的IP，得到可疑IP列表。 通过nc命令，对可疑IP进行端口嗅探 使用ping命令做IP存活监测 import subprocess def ping_ip_range(ip_start, ip_end): for ip_address in range(ip_start, ip_end + 1): ip_to_ping = f\u0026#34;192.168.1.{ip_address}\u0026#34; # 假设我们正在ping192.168.1.x段 # 组织ping命令: ping -c 1 -W 1 192.168.1.1 response = subprocess.run([\u0026#39;ping\u0026#39;, \u0026#39;-c\u0026#39;, \u0026#39;1\u0026#39;, \u0026#39;-W\u0026#39;, \u0026#39;1\u0026#39;, ip_to_ping], stdout=subprocess.PIPE, text=True) # 监测标准输出是否包含\u0026#39;100.0% packet loss\u0026#39;, # 这个要根据实际运行系统调整,有些系统返回的是\u0026#39;100% packet loss\u0026#39; if \u0026#39;100.0% packet loss\u0026#39; not in response.stdout: print(f\u0026#34;{ip_to_ping} is up.\u0026#34;) else: print(f\u0026#34;{ip_to_ping} is down.\u0026#34;) # 使用范例：ping 192.168.1.1 到 192.168.1.254 ping_ip_range(1, 254) 使用netcat命令做端口嗅探 nc -zv 192.168.1.160 1-1024 成功连接到新设备 总结 最后成功找到新设备IP并连接登录设备。以上简单的内网嗅探案例，在实际CTF比赛中应该没这边简单吧（手动狗头)。\n","date":"2024-09-06T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-06","permalink":"https://zfj1441.com/p/%E4%B8%80%E6%AC%A1%E7%AE%80%E5%8D%95%E7%9A%84%E5%86%85%E7%BD%91ip%E7%AB%AF%E5%8F%A3%E5%97%85%E6%8E%A2%E7%BB%8F%E5%8E%86/","title":"一次简单的内网ip端口嗅探经历"},{"content":" 前言 最近《黑神话：悟空》游戏太火了，朋友圈、抖音、blibli等都是天命人，果真是国人的3A大作。莫非游戏行业的风口已经到来？要不搞个游戏试试？继上篇《notebook环境安装及初次体验》，用notebook试试pygame游戏开发。\n游戏开发环境准备 #激活notebook环境，安装pygame库 conda activate notebookEnv pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pygame #游戏中必要要的库 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple random #启动notebook jupyter notebook --notebook-dir=/Users/zfj/workspaces/jupyter 启动开发 学习第一步就是“抄”，从网上找了份pygame扫雷游戏，代码非常精简且完整（准备好资源文件一把就运行成功），顺便做了两处优化。\nimport random import sys import time import pygame # 地雷数量 MINE_COUNT = 99 # 每个方格的大小（宽、高都为20） SIZE = 20 # 方格的行数 BLOCK_ROW_NUM = 16 # 方格的列数 BLOCK_COL_NUM = 30 # 游戏窗口的宽、高 SCREEN_WIDTH, SCREEN_HEIGHT = BLOCK_COL_NUM * SIZE, (BLOCK_ROW_NUM + 2) * SIZE def get_mine_flag_num(board_list): \u0026#34;\u0026#34;\u0026#34; 计算还剩多少颗雷 \u0026#34;\u0026#34;\u0026#34; num = 0 for line in board_list: for num_dict in line: if num_dict.get(\u0026#34;closed_num\u0026#34;) == \u0026#34;雷标记\u0026#34;: num += 1 return num def open_all_mine(board_list): \u0026#34;\u0026#34;\u0026#34; 显示所有的雷 \u0026#34;\u0026#34;\u0026#34; for row, line in enumerate(board_list): for col, num_dict in enumerate(line): if num_dict.get(\u0026#34;opened_num\u0026#34;) == \u0026#34;雷\u0026#34;: num_dict[\u0026#34;opened\u0026#34;] = True def get_mine_num(row, col, board_list): \u0026#34;\u0026#34;\u0026#34; 计算点击的空格周围的雷的数量 \u0026#34;\u0026#34;\u0026#34; # 生成起始位置、终止位置 row_start = row - 1 if row - 1 \u0026gt;= 0 else row row_stop = row + 2 if row + 1 \u0026lt;= BLOCK_ROW_NUM - 1 else row + 1 col_start = col - 1 if col - 1 \u0026gt;= 0 else col col_stop = col + 2 if col + 1 \u0026lt;= BLOCK_COL_NUM - 1 else col + 1 # 循环遍历当前方格周围的雷的数量 mine_num = 0 for i in range(row_start, row_stop): for j in range(col_start, col_stop): if board_list[i][j].get(\u0026#34;opened_num\u0026#34;) == \u0026#34;雷\u0026#34;: mine_num += 1 return mine_num def set_nums_blank(row, col, board_list): \u0026#34;\u0026#34;\u0026#34; 判断当前位置的周边位置是否为空，如果是则继续判断， 最终能够实现点击一个空位置后连续的空位置都能够显示出来 \u0026#34;\u0026#34;\u0026#34; mine_num = get_mine_num(row, col, board_list) # print(\u0026#34;row=%d, col=%d, mine_num=%d\u0026#34; % (row, col, mine_num)) if mine_num == 0: board_list[row][col][\u0026#39;opened\u0026#39;] = True board_list[row][col][\u0026#34;opened_num\u0026#34;] = 0 board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; # 判断对角是否是数字 for i, j in [(-1, -1), (1, 1), (1, -1), (-1, 1)]: if 0 \u0026lt;= row + i \u0026lt;= 15 and 0 \u0026lt;= col + j \u0026lt;= 29: mine_num = get_mine_num(row + i, col + j, board_list) if mine_num: board_list[row + i][col + j][\u0026#39;opened\u0026#39;] = True board_list[row + i][col + j][\u0026#34;opened_num\u0026#34;] = mine_num board_list[row + i][col + j][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; # 判断剩下4个位置是否是也是0，即空 for i, j in [(-1, 0), (1, 0), (0, -1), (0, 1)]: if 0 \u0026lt;= row + i \u0026lt;= 15 and 0 \u0026lt;= col + j \u0026lt;= 29: if not board_list[row + i][col + j].get(\u0026#34;opened\u0026#34;): set_nums_blank(row + i, col + j, board_list) else: board_list[row][col][\u0026#39;opened\u0026#39;] = True board_list[row][col][\u0026#34;opened_num\u0026#34;] = mine_num board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; def left_click_block(row, col, board_list): \u0026#34;\u0026#34;\u0026#34; 左击空格后的处理 \u0026#34;\u0026#34;\u0026#34; if board_list[row][col].get(\u0026#34;opened\u0026#34;) is False and board_list[row][col].get(\u0026#34;opened_num\u0026#34;) != \u0026#34;雷\u0026#34;: # 如果不是雷，那么就计算当前位置数字 mine_num = get_mine_num(row, col, board_list) print(\u0026#34;地雷数:\u0026#34;, mine_num) board_list[row][col][\u0026#34;opened_num\u0026#34;] = mine_num board_list[row][col][\u0026#34;opened\u0026#34;] = True # 标记为\u0026#34;打开\u0026#34;状态 board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; # 标记为\u0026#34;未打开时的状态为空格\u0026#34;，防止显示剩余雷数错误 if mine_num == 0: # 如果方格周边没有雷此时，判断是否有连续空位置 set_nums_blank(row, col, board_list) elif board_list[row][col].get(\u0026#34;opened\u0026#34;) is False and board_list[row][col].get(\u0026#34;opened_num\u0026#34;) == \u0026#34;雷\u0026#34;: board_list[row][col][\u0026#34;opened_num\u0026#34;] = \u0026#34;踩雷\u0026#34; # 标记为\u0026#34;踩雷\u0026#34;图片 board_list[row][col][\u0026#34;opened\u0026#34;] = True # 标记为\u0026#34;打开\u0026#34;状态 board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; # 标记为\u0026#34;未打开时的状态为空格\u0026#34;，防止显示剩余雷数错误 return True def create_random_board(row, col, mine_num): \u0026#34;\u0026#34;\u0026#34; 得到一个随机的棋盘 \u0026#34;\u0026#34;\u0026#34; # 随机布雷 nums = [{\u0026#34;opened\u0026#34;: False, \u0026#34;opened_num\u0026#34;: 0, \u0026#39;closed_num\u0026#39;: \u0026#34;空\u0026#34;} for _ in range(row * col - mine_num)] # 16x30-99 表示的是生成381个0 nums += [{\u0026#34;opened\u0026#34;: False, \u0026#34;opened_num\u0026#34;: \u0026#34;雷\u0026#34;, \u0026#39;closed_num\u0026#39;: \u0026#34;空\u0026#34;} for _ in range(mine_num)] # 99颗地雷 random.shuffle(nums) # 乱序，此时nums是乱的 return [list(x) for x in zip(*[iter(nums)] * col)] def right_click_block(row, col, board_list): \u0026#34;\u0026#34;\u0026#34; 右击方格后更新其状态（标记为雷、问号?、取消标记） \u0026#34;\u0026#34;\u0026#34; if board_list[row][col].get(\u0026#34;opened\u0026#34;) is False: if board_list[row][col][\u0026#34;closed_num\u0026#34;] == \u0026#34;空\u0026#34;: board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;雷标记\u0026#34; elif board_list[row][col][\u0026#34;closed_num\u0026#34;] == \u0026#34;雷标记\u0026#34;: board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;疑问标记\u0026#34; elif board_list[row][col][\u0026#34;closed_num\u0026#34;] == \u0026#34;疑问标记\u0026#34;: board_list[row][col][\u0026#34;closed_num\u0026#34;] = \u0026#34;空\u0026#34; def click_block(x, y, board_list): \u0026#34;\u0026#34;\u0026#34; 检测点击的是哪个方格（即第x行，第y列） \u0026#34;\u0026#34;\u0026#34; # 计算出点击的空格的行、列 for row, line in enumerate(board_list): for col, _ in enumerate(line): if col * SIZE \u0026lt;= x \u0026lt;= (col + 1) * SIZE and (row + 2) * SIZE \u0026lt;= y \u0026lt;= (row + 2 + 1) * SIZE: print(\u0026#34;点击的空格的位置是:\u0026#34;, row, col) return row, col def check_success(board_list): \u0026#34;\u0026#34;\u0026#34; 监测是否胜利 \u0026#34;\u0026#34;\u0026#34; num = 0 for row, line in enumerate(board_list): for col, num_dict in enumerate(line): if num_dict.get(\u0026#34;opened_num\u0026#34;) == \u0026#34;雷\u0026#34; and num_dict.get(\u0026#34;closed_num\u0026#34;) == \u0026#34;雷标记\u0026#34;: num += 1 return num == MINE_COUNT def run(screen): bgcolor = (225, 225, 225) # 背景色 # 要显示的棋盘 # board_list = [[0] * BLOCK_COL_NUM for _ in range(BLOCK_ROW_NUM)] board_list = create_random_board(BLOCK_ROW_NUM, BLOCK_COL_NUM, MINE_COUNT) # 16行、30列，有99颗地雷 # 默认的方格图片 img_blank = pygame.image.load(\u0026#39;resource/blank.bmp\u0026#39;).convert() img_blank = pygame.transform.smoothscale(img_blank, (SIZE, SIZE)) # \u0026#34;雷标记\u0026#34;图片 img_mine_flag = pygame.image.load(\u0026#39;resource/flag.bmp\u0026#39;).convert() img_mine_flag = pygame.transform.smoothscale(img_mine_flag, (SIZE, SIZE)) # \u0026#34;雷\u0026#34;图片 img_mine = pygame.image.load(\u0026#39;resource/mine.bmp\u0026#39;).convert() img_mine = pygame.transform.smoothscale(img_mine, (SIZE, SIZE)) # \u0026#34;疑问标记\u0026#34;图片 img_ask = pygame.image.load(\u0026#39;resource/ask.bmp\u0026#39;).convert() img_ask = pygame.transform.smoothscale(img_ask, (SIZE, SIZE)) # \u0026#34;踩雷\u0026#34;图片 img_blood = pygame.image.load(\u0026#39;resource/blood.bmp\u0026#39;).convert() img_blood = pygame.transform.smoothscale(img_blood, (SIZE, SIZE)) # \u0026#34;表情\u0026#34;图片 face_size = int(SIZE * 1.25) img_face_fail = pygame.image.load(\u0026#39;resource/face_fail.bmp\u0026#39;).convert() img_face_fail = pygame.transform.smoothscale(img_face_fail, (face_size, face_size)) img_face_normal = pygame.image.load(\u0026#39;resource/face_normal.bmp\u0026#39;).convert() img_face_normal = pygame.transform.smoothscale(img_face_normal, (face_size, face_size)) img_face_success = pygame.image.load(\u0026#39;resource/face_success.bmp\u0026#39;).convert() img_face_success = pygame.transform.smoothscale(img_face_success, (face_size, face_size)) # \u0026#34;表情\u0026#34;位置 face_pos_x = (SCREEN_WIDTH - face_size) // 2 face_pos_y = (SIZE * 2 - face_size) // 2 # 雷的数量图片 img0 = pygame.image.load(\u0026#39;resource/0.bmp\u0026#39;).convert() img0 = pygame.transform.smoothscale(img0, (SIZE, SIZE)) img1 = pygame.image.load(\u0026#39;resource/1.bmp\u0026#39;).convert() img1 = pygame.transform.smoothscale(img1, (SIZE, SIZE)) img2 = pygame.image.load(\u0026#39;resource/2.bmp\u0026#39;).convert() img2 = pygame.transform.smoothscale(img2, (SIZE, SIZE)) img3 = pygame.image.load(\u0026#39;resource/3.bmp\u0026#39;).convert() img3 = pygame.transform.smoothscale(img3, (SIZE, SIZE)) img4 = pygame.image.load(\u0026#39;resource/4.bmp\u0026#39;).convert() img4 = pygame.transform.smoothscale(img4, (SIZE, SIZE)) img5 = pygame.image.load(\u0026#39;resource/5.bmp\u0026#39;).convert() img5 = pygame.transform.smoothscale(img5, (SIZE, SIZE)) img6 = pygame.image.load(\u0026#39;resource/6.bmp\u0026#39;).convert() img6 = pygame.transform.smoothscale(img6, (SIZE, SIZE)) img7 = pygame.image.load(\u0026#39;resource/7.bmp\u0026#39;).convert() img7 = pygame.transform.smoothscale(img7, (SIZE, SIZE)) img8 = pygame.image.load(\u0026#39;resource/8.bmp\u0026#39;).convert() img8 = pygame.transform.smoothscale(img8, (SIZE, SIZE)) img_dict = { 0: img0, 1: img1, 2: img2, 3: img3, 4: img4, 5: img5, 6: img6, 7: img7, 8: img8, \u0026#39;雷标记\u0026#39;: img_mine_flag, \u0026#39;雷\u0026#39;: img_mine, \u0026#39;空\u0026#39;: img_blank, \u0026#39;疑问标记\u0026#39;: img_ask, \u0026#39;踩雷\u0026#39;: img_blood, } img_success = pygame.image.load(\u0026#34;resource/success.png\u0026#34;).convert_alpha() # 标记是否踩到雷 game_over = False # 游戏状态 game_status = \u0026#34;normal\u0026#34; # 显示雷的数量、耗时用到的资源 font = pygame.font.Font(\u0026#39;resource/a.TTF\u0026#39;, SIZE * 2) # 字体 f_width, f_height = font.size(\u0026#39;999\u0026#39;) red = (200, 40, 40) # 标记出雷的个数 flag_count = 0 # 记录耗时 elapsed_time = 0 last_time = time.time() start_record_time = False # 创建计时器（防止while循环过快，占用太多CPU的问题） clock = pygame.time.Clock() while True: # 事件检测（鼠标点击、键盘按下等） for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() elif event.type == pygame.MOUSEBUTTONDOWN and event.button: b1, b2, b3 = pygame.mouse.get_pressed() mouse_click_type = None if b1 and not b2 and not b3: # 左击 mouse_click_type = \u0026#34;left\u0026#34; elif not b1 and not b2 and b3: # 右击 mouse_click_type = \u0026#34;right\u0026#34; print(\u0026#34;点击了鼠标的[%s]键\u0026#34; % mouse_click_type) x, y = pygame.mouse.get_pos() if game_status == \u0026#34;normal\u0026#34; and 2 * SIZE \u0026lt;= y \u0026lt;= SCREEN_HEIGHT: # 计算点击的是哪个空 position = click_block(x, y, board_list) if position: if mouse_click_type == \u0026#34;right\u0026#34;: # 如果右击方格，那么就更新其状态 right_click_block(*position, board_list) # 更新标记的雷的数量 flag_count = get_mine_flag_num(board_list) start_record_time = True # 开始记录耗时 elif mouse_click_type == \u0026#34;left\u0026#34;: # 点击空格的处理 game_over = left_click_block(*position, board_list) print(\u0026#34;是否踩到雷\u0026#34;, game_over) start_record_time = True # 开始记录耗时 # 更新标记的雷的数量 flag_count = get_mine_flag_num(board_list) if game_over: # 将所有雷的位置，标记出来 open_all_mine(board_list) # 更改游戏状态 game_status = \u0026#34;fail\u0026#34; # 停止记录耗时 start_record_time = False elif face_pos_x \u0026lt;= x \u0026lt;= face_pos_x + face_size and face_pos_y \u0026lt;= y \u0026lt;= face_pos_y + face_size: # 重来一局 print(\u0026#34;点击了再来一局...\u0026#34;) return # 填充背景色 screen.fill(bgcolor) # 显示方格 for i, line in enumerate(board_list): for j, num_dict in enumerate(line): if num_dict.get(\u0026#34;opened\u0026#34;): screen.blit(img_dict[num_dict.get(\u0026#34;opened_num\u0026#34;)], (j * SIZE, (i + 2) * SIZE)) else: screen.blit(img_dict[num_dict.get(\u0026#34;closed_num\u0026#34;)], (j * SIZE, (i + 2) * SIZE)) # 显示表情 if game_status == \u0026#34;win\u0026#34;: screen.blit(img_face_success, (face_pos_x, face_pos_y)) elif game_status == \u0026#34;fail\u0026#34;: screen.blit(img_face_fail, (face_pos_x, face_pos_y)) else: screen.blit(img_face_normal, (face_pos_x, face_pos_y)) # 显示剩余雷的数量 mine_text = font.render(\u0026#39;%02d\u0026#39; % (MINE_COUNT - flag_count), True, red) screen.blit(mine_text, (30, (SIZE * 2 - f_height) // 2 - 2)) # 显示耗时 if game_status != \u0026#34;win\u0026#34; and start_record_time and time.time() - last_time \u0026gt;= 1: elapsed_time += 1 last_time = time.time() mine_text = font.render(\u0026#39;%03d\u0026#39; % elapsed_time, True, red) screen.blit(mine_text, (SCREEN_WIDTH - f_width - 30, (SIZE * 2 - f_height) // 2 - 2)) # 显示胜利 if check_success(board_list): game_status = \u0026#34;win\u0026#34; sucess_width, success_height = img_success.get_size() success_pos_x = (SCREEN_WIDTH - sucess_width) // 2 success_pos_y = (SCREEN_HEIGHT - success_height) // 2 screen.blit(img_success, (success_pos_x, success_pos_y)) # 刷新显示（此时窗口才会真正的显示） pygame.display.update() # FPS（每秒钟显示画面的次数） clock.tick(60) # 通过一定的延时，实现1秒钟能够循环60次 def main(): \u0026#34;\u0026#34;\u0026#34; 循环调用run函数，每调用一次就重新来一局游戏 \u0026#34;\u0026#34;\u0026#34; pygame.init() pygame.display.set_caption(\u0026#39;扫雷\u0026#39;) screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) while True: run(screen) if __name__ == \u0026#39;__main__\u0026#39;: main() 总结 通过notebook环境和pygame库可以快速体验做游戏开发的快乐。但想做好一款游戏可没这么简单，好的游戏创意和游戏设计才是重点。\n完整游戏代码和资源\n","date":"2024-09-03T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-09-03","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8notebook%E6%90%AD%E5%BB%BA%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83/","title":"使用notebook搭建游戏开发环境"},{"content":"前言 在《乔布斯传》一书中写道到苹果公司和微软公司先后在1984年1985年先后将图形界面引入到操作系统中，这一举措大大降低个人电脑的实用门槛，推动了个人电脑在各行各业的普及。但是图形界面真的有提升效率吗？答案是否定的。作为一名应用开发人员我很清楚大部分时候命令行、快捷键远远比鼠标操作来的效率高。你会发现一些word大佬、cad大佬、PS大神在工作中都是眼花撩乱的快捷键、命令行，记得第一次在打印店看到老板那一顿猛操作，让我不禁暗自吐出“牛X”二字。所以我收集整理些应用快捷键学习向大佬们看齐。\nChrome浏览器常用快捷键 操作 windows或linux快捷键 macOS快捷键 刷新网页 F5 或 Ctrl + R Command + R 页面中搜索 Ctrl + F Command + F 打开新标签页 Ctrl + T Command + T 关闭当前标签页 Ctrl + W Command + W 指定标签页切换 Alt + 数字 Command + 数字 恢复关闭的标签页 Ctrl + Shift + T Command + Shift + T 隐身模式打开新窗口 Ctrl + Shift + N Command + Shift + N 打开下载页面 Ctrl + J Command + Shift + J 打开历史记录 Ctrl + H Command + Y 当前页面前进/后退 Alt + 左箭头 / Alt + 右箭头 Command + [ / Command + ] 向下滚动一页 Space 或 Page Down Space 向上滚动一页 Shift + Space 或 Page Up Shift + Space 打开开发者工具 Ctrl + Shift + J 或 F12 Command + Option + I word常用快捷键 操作 windows或linux快捷键 macOS快捷键 光标移至首部/尾部 Ctrl + Home/Ctrl + End 重复上一操作 F4 放大/缩小字号 Ctrl + ]/Ctrl + [ 一倍行距 Ctrl + 1 两倍行距 Ctrl + 2 1.5倍行距 Ctrl + 5 标题1 Ctrl + Alt + 1 标题2 Ctrl + Alt + 2 标题3 Ctrl + Alt + 3 居中对齐 Ctrl + E 左对齐 Ctrl + L 右对齐 Ctrl + R 分散对齐 Ctrl + Shift + J 复制样式 Ctrl + Shift + C 粘贴样式 Ctrl + Shift + V Photoshop常用快捷键 操作 windows或linux快捷键 macOS快捷键 自由变换 CTRL + T 复制图层 CTRL + J 合并图层 CCTRL + E 全选 Ctrl + A 反选 CTRL + SHIFT + I 羽化 CTRL + ALT + D 矩形、椭圆选框工具 M 裁剪工具 C 移动工具 V 套索、多边形套索、磁性套索 L 魔棒工具 W 画笔工具 B 铅笔、直线工具 N 缩放工具 Z 油漆桶工具 K 度量工具 U 总结 快捷键内容来自互联网，结合自己平时使用习惯挑选了常用的几个，如果需要更多内容可以查看一下参考内容。\n参考 谷歌浏览器常用快捷键有哪些? Chrome 键盘快捷键大全\nWord常用快捷键有哪些? Word最常用的20个通用快捷键汇总\nphotoshop之中有哪些常用的快捷键\n","date":"2024-08-26T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-08-26T22:37:26+08:00","permalink":"https://zfj1441.com/p/chromewordphotoshop%E5%B8%B8%E7%94%A8%E5%BF%AB%E6%8D%B7%E9%94%AE%E6%95%B4%E7%90%86/","title":"chrome、Word、Photoshop常用快捷键整理"},{"content":"前言 2022年接触到玩客云，那时玩客云内置迅雷会员下载速度贼快，我也是因为这个原因买的。但好景不长2024年3月玩客云官方通知玩客云停服。目前app无法登陆，远程下载就更没戏了。设备一直闲置状态，最近看网上推荐可以跑PCDN赚电费，看博主收益情况每天有1元以上的收益。跑着尝试的态度接入，看能把NAS电费赚回来不。\n设备及网络状况 100兆联通线路（30Mb上行，70Mb下行） 无公网，有IPV6 开启UPNP 500G机械硬盘 玩客云 玩客云系统停服后默认接入网心云PCDN，下载好APP注册，购买KEY绑定设备，开跑……。经过两天后设备缓存已经达到70G，但收益不堪入目。一开始我以为是缓存不够导致收益低，就这样跑一个多月，收益也不怎么样，收益如图1。 成本收益 成本投入主要是玩客云设备、家庭宽带、电费、风险投资（家庭PCDN运营商是禁止，严重时要请去喝茶的）。玩客云功耗基本5W左右，24小时开机，家庭用电每千瓦小时0.62元，那么30天的电费计算 5 * 24 / 1000 * 0.62 * 30 = 2.232元\n总结 家庭网络跑PCDN赚电费，也就能赚回玩客云本身的用电，赚NAS以及全家设备的电费现在这情况基本不可能了。后面了解到：具体收益和宽带运营商、是否公网、硬盘类型等多种因素有关。要想收益高得一步步调整优化，比如硬盘换成240SSD；申请开通公网ip等等。\n","date":"2024-08-24T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-08-24T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%AE%9E%E6%B5%8B1%E4%B8%AA%E6%9C%88%E7%94%A8%E5%AE%B6%E7%94%A8%E7%BD%91%E7%BB%9C%E8%B7%91pcdn%E8%B5%9A%E7%94%B5%E8%B4%B9/","title":"实测1个月用家用网络跑pcdn赚电费"},{"content":" 前言 今天回家发现电视柜凌乱的电子设备，心血来潮统计下这些年经手的电子玩具。有道是生命不息折腾不止，在此记录下曾经玩的电子设备以及截止今天（2024/08/20）这些设备的最终归宿。\n设备汇总 设备型号 状态 描述 树莓派3B 服役中 安装homeassistant做智能家居主控 斐讯K1s 战损 斐讯K2 吃灰 斐讯K2p 吃灰 斐讯K3 服役中 服役中与ac86u组mesh 斐讯R1 吃灰 京东回血 斐讯A1 服役中 刷机接入homeassistant 斐讯M1 服役中 刷机接入homeassistant 斐讯S7 服役中 刷机接入homeassistant 斐讯N1 吃灰 闲鱼回血 斐讯DC1 服役中 刷机接入homeassistant 我家云 吃灰 闲鱼回血 玩客云 吃灰 蜗牛星际 服役中 黑裙灰，私人NAS网盘 随身wifi 吃灰 小米Pad2 服役中 windows10系统，homeassistant显示 iPad mini4 服役中 homeassistant家庭中枢 小米温度计2 服役中 刷机接入homeassistant 艾韵A1 服役中 刷机接入homeassistant 总结 差点就集齐斐讯家所有产品了（手动狗头）。生命不息折腾不止，祝各位玩机愉快。\n","date":"2024-08-20T22:06:59+08:00","image":"https://picsum.photos/800/600?r=2024-08-20T22:06:59+08:00","permalink":"https://zfj1441.com/p/%E8%AE%B0%E5%BD%95%E4%B8%8B%E6%9B%BE%E7%BB%8F%E7%9A%84%E7%94%B5%E5%AD%90%E7%8E%A9%E5%85%B7/","title":"记录下曾经的电子玩具"},{"content":"前言 最近参加了单位组织的数据分析竞赛，用到notebook、pandas、numpy等一些常用的工具和库。但平时工作这些用的都比较少，只能赛前零时抱佛脚试试，在这记录下本地windows10安装notebook简要过程。\n环境安装 1.windows10系统安装miniconda mimicodna安装包\nMiniconda3-py310_24.3.0-0-Windows-x86_64.exe下载安装包，注意修改安装路径，之后下一步下一步（全默认） 修改环境变量 2.创建notebook环境、安装jupyter notebook并配置 conda create -n notebookEnv python==3.10 conda activate notebookEnv # 将notebookEnv虚拟环境加入到notebook中 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyter pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ipykernel python -m ipykernel install --user --name=notebookEnv # notebook中文包 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jupyterlab-language-pack-zh-CN # 常用的数据分析库 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas 3.启动notebook conda activate notebookEnv jupyter notebook --notebook-dir=D:\\workspace\\jupyter #notebook-dir为工作路径 浏览器访问http://localhost:8888/\n4.最后可以写个bat实现一键启动notebook @echo off cd /d D:\\\\workspace\\\\jupyter cmd /k \u0026#34;conda activate notebookEnv \u0026amp;\u0026amp; jupyter notebook --notebook-dir=D:\\workspace\\jupyter\u0026#34; 总结 安装并试用几天后，notebook让我眼前一亮。不仅可以做数据分析写简单的python脚本，而且可以写markdown，这就非常有意思了。因为我偶尔更新下博客，更新频率也非常低，一个月或一个季度一次。目前使用的是obsidian，这玩意怎么说呢功能很多插件丰富，但是作为低频写手来说工具还是有些麻烦。而浏览器上使用的notebook倒是可以试试，看看能否提升日常效率。\n参考 使用conda安装jupyter、将conda环境写入notebook的kernel中\nPandas常用的35个经典操作 bat脚本中激活conda环境后无法执行其他命令\n","date":"2024-08-11T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-08-11T01:17:29+08:00","permalink":"https://zfj1441.com/p/notebook%E7%8E%AF%E5%A2%83%E5%AE%89%E8%A3%85%E5%8F%8A%E5%88%9D%E6%AC%A1%E4%BD%93%E9%AA%8C/","title":"notebook环境安装及初次体验"},{"content":"前言 有服务器的朋友多少都知道，黑客真的无处不在，无时无刻都在扫描你的服务器。记得第一次购买服务器的时候，那时安全意识弱什么sshd、mysql都是默认端口，一天下来ssh被黑客爆破上千次。80、443端口服务就更不用说了每天都有异常请求。那么如何防御这些黑客的攻击呢？\n发现黑客攻击行为 ssh爆破行为检查，使用root用户输入lastb命令可以查看登陆失败的用户 nginx代理的http、https服务检查，可以查看nginx服务日志，会发现有很多异常的请求如下图 使用fail2ban防御 fail2ban介绍 fail2ban通过监控日志文件来自动检测和禁止恶意IP地址，从而保护服务器免受暴力破解等攻击。常见应用场景：ssh防爆破、web服务SQL注入、跨站攻击、邮件服务器暴力破解等。\nfail2ban安装使用 这里我以centos7系统、iptables防火墙，配置fail2ban防止ssh爆破为例\n# 安装 sudo yum install epel-release sudo yum install fail2ban # 复制默认配置并修改sshd部分，主要是日志路径和端口 cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local vim /etc/fail2ban/jail.local [sshd] port = 11223 #sshd端口 logpath = /var/log/secure #监测的日志路径 backend = %(sshd_backend)s enabled = true #是否启用 # 启动 systemctl restart fail2ban # 检查服务状态 fail2ban-client status sshd #删除被ban IP fail2ban-client set sshd unbanip 192.168.1.11 #查看iptables策略 iptables -vnL 测试验证 使用ssh命令模拟多次登录，在4次输入错误密码后，服务器已无响应。再次连接提示失败。fail2ban已经正常工作，通过服务器ipables策略可以看到已经增加了一条禁止策略。 异常排查 fail2ban-client status sshd 命令提示“\u0026lsquo;sshd\u0026rsquo; does not exist”。1）请检查sshd配置中日志文件/etc/log/secure是否存在 2）请复制默认jail.conf在修改 操作前备份iptables配置/etc/sysconfig/iptables，我在操作过程中iptables配置莫名丢失 结论 这里以防御ssh爆破为例简单介绍了下fail2ban的使用，其实还有很多其他实用防御场景。\n20250330更新 最近发现有些ip每隔10分钟尝试爆破一次，fail2ban效果不好。所以写个脚步把这些ip手工加入黑名单。重启iptables会失效，需要保存到iptables配置文件中才可以持久化\n#!/bin/bash # 使用iptables禁止ip访问 printUsage() { echo \u0026#34;用法:\u0026#34; echo \u0026#34; $0 ban/unban IP\u0026#34; exit -1 } if [ $# -ne 2 ]; then printUsage fi if [ \u0026#34;$1\u0026#34; == \u0026#34;ban\u0026#34; ];then read -p \u0026#34;确认禁止IP[$2]吗?(y/n):\u0026#34; confirm if [[ \u0026#34;$confirm\u0026#34; =~ ^[Yy]$ ]]; then iptables -I INPUT -p tcp -s $2 -j DROP else echo \u0026#34;用户取消，退出....\u0026#34; exit 1 fi exit fi if [ \u0026#34;$1\u0026#34; == \u0026#34;unban\u0026#34; ];then read -p \u0026#34;确认解禁IP[$2]吗?(y/n):\u0026#34; confirm if [[ \u0026#34;$confirm\u0026#34; =~ ^[Yy]$ ]]; then iptables -D INPUT -p tcp -s $2 -j DROP else echo \u0026#34;用户取消，退出....\u0026#34; exit 1 fi exit fi printUsage #查看策略： #iptables -nvL #汇总爆破ip： #lastb | head -n 1000 | awk \u0026#39;{print $3}\u0026#39; | sort | uniq -c #centos7保存iptables配置 #iptables-save \u0026gt; /etc/sysconfig/iptables 参考 centos7 部署 fail2ban\nFail2ban无法启用sshd jail\nGitHub\n","date":"2024-07-13T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-07-13T21:52:22+08:00","permalink":"https://zfj1441.com/p/%E6%88%91%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%98%B2%E6%8A%A4-fail2ban%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/","title":"我的服务器防护-fail2ban使用记录"},{"content":"前言 昨天网上突然爆发了OpenSSH漏洞（CVE-2024-6387）这漏洞贼厉害，版本8.5p1~9.7p1的通过该漏洞可以获得getshell。看这消息我立马登上服务瞧了一眼。嘿嘿，我这CentOS7用的还是老版本，不受影响。正当我偷乐的时候，看到评论说7.4p1有CVE-2020-15778、CVE-2020-14145等6个漏洞，这又让我紧张起来。\n#ssh -V OpenSSH_7.4p1, OpenSSL 1.0.2k-fips 26 Jan 2017 漏洞情况 虽然我服务器没什么价值，黑了也无所谓，但万一拿我服务器去当肉鸡我这绝对不允许。为确保我的服务器安全还是了解了下以上漏洞，看看是否需要修复。\n漏洞编号 漏洞等级 影响版本 漏洞描述 CVE-2020-15778 高危 \u0026lt;=openssh-8.3p1 执行带有payload的scp命令，可获得getshell,前提是得知道ssh密码 CVE-2020-14145 中等 5.7版本至8.3版本 ssh客户端具有可观察到的差异，导致算法协商中的信息泄漏 CVE-2017-15906 中等 \u0026lt;7.6 该漏洞源于程序在只读模式下未能正确的阻止写入操作。攻击者可利用该漏洞创建长度为零的文件 CVE-2018-15919 高危 5.7~7.8 PoC已公布，攻击者可利用此漏洞猜测用户名 CVE-2018-15473 高危 5.7~7.7 PoC已公布，攻击者可利用此漏洞猜测用户名 CVE-2021-41617 6.2到8.7版本 根据系统配置的不同，继承的组可能会让辅助程序获得意外的权限，导致权限提升 总结 通过以上漏洞信息，黑客可以猜测到我的用户，但是想getshell需要再费点劲。我这小服务升级OpenSSH再等等吧，centos7停服，说不定哪天得换系统了。\n参考 OpenSSH 命令注入漏洞（CVE-2020-15778）\nIVS3800 漏洞扫描出来了 CVE-2020-14145漏洞该怎么办 OpenSSH设计漏 OpenSSH用户名枚举漏洞（CVE-2018-15473、CVE-2018-. 5919）安全预警通告 关于OpenSSH 权限提升漏洞(CVE-2021-41617)的预警提示\n","date":"2024-07-02T22:06:59+08:00","image":"https://picsum.photos/800/600?r=2024-07-02T22:06:59+08:00","permalink":"https://zfj1441.com/p/%E5%85%B3%E4%BA%8Eopenssh%E6%BC%8F%E6%B4%9Ecve-2024-6387%E4%BF%AE%E5%A4%8D/","title":"关于OpenSSH漏洞(CVE-2024-6387)修复"},{"content":"前言 在现代工作和学习环境中，绘图工具的重要性不言而喻。无论是项目管理、流程图、组织结构图，还是软件架构图，好的绘图工具都能大大提升效率和可视化效果。今天，我想向大家推荐一款强大的在线绘图工具——draw.io。\n来看看drawio画的图\n什么是draw.io？ draw.io是一款免费的在线绘图软件，用户无需下载任何软件，只需通过浏览器即可使用。它不仅支持各种类型的图表绘制，还具备简单易用的界面和强大的功能，使其成为广大用户的理想选择。\ndraw.io的主要特点 完全免费：draw.io提供了完全免费的服务，无需任何付费即可使用所有功能。这对于个人用户和小型团队来说，尤其具有吸引力。\n广泛的模板库：draw.io内置了丰富的模板库，涵盖流程图、网络图、组织结构图、UML图、ER图等多种类型，用户可以根据需要快速创建所需图表。\n强大的兼容性：draw.io支持与Google Drive、OneDrive、Dropbox等多种云存储服务集成，方便用户随时随地访问和编辑图表。此外，它还能导入和导出多种文件格式，包括XML、PNG、SVG、PDF等。\n协作功能：draw.io允许多个用户同时编辑同一图表，便于团队协作和实时讨论，极大提高了工作效率。\n离线模式：虽然draw.io是一款在线工具，但它同样支持离线使用。用户可以下载其桌面版，在没有网络连接时依然可以进行绘图操作。\n使用draw.io的优势 易上手：draw.io的用户界面非常直观，即使是没有绘图经验的用户也能快速上手，轻松创建专业级别的图表。 高效生产力：通过提供丰富的模板和强大的功能，draw.io能大大节省用户的绘图时间，提升工作效率。 安全可靠：draw.io注重用户数据的安全性，所有的图表数据都可以加密存储，用户可以放心使用。 适用场景 项目管理：通过绘制甘特图、流程图和任务分配图表，帮助项目经理更好地规划和管理项目进度。 软件开发：支持UML图和ER图的绘制，有助于开发人员进行软件设计和数据库建模。 企业管理：组织结构图和业务流程图的绘制，为企业管理者提供直观的组织和业务流程展示。 教育培训：教师和培训师可以利用draw.io创建各种教学和培训图表，提升教学效果。 结语 draw.io以其简单易用、功能强大和完全免费的特点，成为了广大用户心目中的最佳绘图工具之一。如果你正在寻找一款高效、可靠的绘图软件，不妨试试draw.io，相信它会成为你工作和学习中的得力助手。\n立即访问draw.io，开始你的绘图之旅吧！\n","date":"2024-06-29T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-06-29T01:17:29+08:00","permalink":"https://zfj1441.com/p/%E6%9C%80%E4%BD%B3%E7%94%BB%E5%9B%BE%E8%BD%AF%E4%BB%B6%E6%8E%A8%E8%8D%90draw.io/","title":"最佳画图软件推荐draw.io"},{"content":"前言 续《通过IPv6随时随地访问家里的NAS》，前提是光猫路由器都开启IPv6功能。但有时候光猫、路由器并不默认没开启IPv6功能，需要手工设置。本篇就详细说下我是如何开启IPv6功能。\n设置ipv6功能 在设置前简单了解下常见家庭网络结构:\n场景一，全能型光猫，光猫负责光信号转电信号、拨号上网、无线Wi-Fi。\n场景二，光猫负责光信号转电信号、拨号上网。路由器负责路由、无线Wi-Fi。\n场景三，光猫负责光信号转电信号，路由器负责拨号上网、路由、无线Wi-Fi。\n因光猫、路由器系统众多所以无法给出统一的设置教程。这里给个设置思路：按网络结构可以从下往上倒序设置：\n设置PC电脑或NAS服务器，开启获取ipv6地址。检查是否成功获取ipv6地址，如果成功获取到IPv6地址说明你的光猫、路由器都默认已开启ipv6功能，不需要再过多设置。 如果PC电脑或NAS服务器开启获取ipv6地址后，无法获取IPv6地址。则需要进路由器管理界面开启ipv6功能。 如果路由器开启ipv6功能后还是不成功，再设置光猫。 我可能比较幸运，在设置好服务器、路由器就成功获取到了ipv6地址。我的路由器设置如下：联机类型选择“Native”，其他都默认。“内部网络IPv6地址“、”内部网络IPv6前缀“能获取到内容就说明开启IPv6成功。\n开启ipv6防火墙 因为开启IPv6功能后默认是公网IPv6地址，家里所有电脑将全部暴露，会有一定安全隐患。所以为安全建议开启IPv6防火墙，只放通服务器的IP端口。 使用技巧 ipv6防火墙设置，IPv6地址后四段是服务器Mac地址，所以防火墙策略地址可以写成 ::1111:1111:1112:3456 光猫、路由器重启后IPv6地址会发生变化且IPv6地址很长很难记，后期可以设置dns用域名访问 光猫开启IPv6有两种途径\n1）破解光猫获取管理密码然后进光猫设置。搜素对应的光猫型号，会有很多教程。 2）直接拨打宽带运营商客户电话要求开通ipv6。（推荐方法） 手机偶尔无法正常获取ipv6地址，可以开关下飞行模式重新注册下网络。 最后 通过ipv6可以很轻松的访问内网服务器，但方便自己也会方便黑客，所以强烈建议开启防火墙。以上全部分享内容，希望对你有所帮助。\n","date":"2024-06-13T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-06-13T01:17:29+08:00","permalink":"https://zfj1441.com/p/%E8%AE%BE%E7%BD%AE%E5%85%89%E7%8C%AB%E8%B7%AF%E7%94%B1%E5%99%A8%E5%BC%80%E5%90%AFipv6/","title":"设置光猫路由器开启IPv6"},{"content":"前言 最近在闲鱼淘了个小米Pad2，刷了windows10系统挂墙上做智能家居中控。pad配合人体感应实现人到自动亮屏幕，体验非常棒。但有个问题windows10长时间不操作就自动锁屏、显示登录界面，这让我很头疼，经过几天折腾研究也是终于解决，遂写个博客记录下。\n关闭锁屏 通过组策略，可以很方便地将Win10锁屏跳过，但并不能跳过登录面板，这项功能比较适合于办公机或者公共电脑。\nwin+R调出运行，输入“gpedit.msc”打开组策略编辑器； 依次找到并点击“计算机配置”→“管理模板”→“控制面板”→“个性化”； 在右侧窗格找到“不显示锁屏”，双击它，修改其默认值为“已启用”； 关闭登录 win+R调出运行，，输入“netplwiz”打开用户账户面板; 点击当前正在使用的账号名，取消“要使用本计算机，用户必须输入用户名和密码”复选框； 点击“确定”按钮，出于安全性考虑，系统此刻会要求您输入一次密码以便确认身份； 设置完成后重新启动电脑，这时Win10就会自动跳过登录环节，直接进入桌面了。 关闭唤醒密码 通过以上步骤重新开机后就不需要密码，但是长时间不操作pad亮屏后还是需要密码。请按下面操作完美解决\nwin+R调出运行，输入“gpedit.msc”打开组策略编辑器； 依次找到并点击“计算机配置”→“管理模板”→“系统”→“电源管理”-\u0026gt;“睡眠设置”； 在右侧窗格找到“唤醒计算机时需要密码（接通电源）”、“关闭混合睡眠（接通电源）”、“唤醒计算机时需要密码（使用电池）”、“关闭混合睡眠（使用电池）”，双击它，修改其默认值为“已禁用”； 最后 完成以上操作，你的windows10就在也不用密码登录。\n参考 教你关闭Windows 10锁屏\n","date":"2024-06-06T00:00:00Z","image":"https://picsum.photos/800/600?mom=302\u0026r=win10","permalink":"https://zfj1441.com/p/windows10%E5%85%B3%E9%97%AD%E9%94%81%E5%B1%8F%E5%85%B3%E9%97%AD%E7%99%BB%E9%99%86/","title":"windows10关闭锁屏关闭登陆"},{"content":"前言 上一篇分享《我的家用NAS应用场景》，其中最大的难题是公网访问。如果你和我一样没有公网ipv4地址，但又有访问家里NAS或windows电脑的需求，不妨了解下ipv6访问内网服务。\n简单介绍下什么是ipv6？\n随着互联网的迅猛发展，IPv4地址的耗尽已经成为全球网络面临的一大难题。为了解决这一问题，IPv6（Internet Protocol Version 6）应运而生。IPv6不仅提供了庞大的地址空间，还具备自动配置、安全性增强、简化路由等多种技术优势。\n必要知识 因ipv6地址兼容性问题，手机、服务器必需都有IPV6地址才能相互访问。目前电信、移动、联通、广电运营商蜂窝网、宽度都已至此ipv6，只要简单设置就能轻松实现远程访问。\n检查手机是否支持ipv6 手机浏览器打开ipv6测试如果最后显示10/10 ，说明当地运营商已经支持ipv6并获取到IPv6地址，如果显示其他不妨先开关下飞行模式，重新注册下网络再做IPv6测试。 检查服务器是否支持IPv6 群辉系统 群晖NAS打开IPV6地址、并检查是否成功获取IPV6地址 windows系统 windows打开IPV6地址、并检查是否成功获取IPV6地址 外网访问 使用ipv6地址访问服务，格式如: http://[ipv6地址]:服务端口 http://[2408:1111:1111:1111:1111:1111:1112:3456]:5000 使用Windows远程方式时输入地址\n[2408:1111:1111:1111:1111:1111:1112:3456]:3389 最后 如果你的光猫、路由器 都默认打开ipv6地址，按以上步骤你基本就可以通过手机访问你的服务器。如果群晖或windows打开ipv6但无法获取ipv6地址，你可能需要继续看下一篇《设置光猫路由器开启IPv6》\n参考 IPv6 介绍（概述）\nwin10系统如何设置启动ipv6\n","date":"2024-06-06T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-06-06T01:17:29+08:00","permalink":"https://zfj1441.com/p/%E9%80%9A%E8%BF%87ipv6%E9%9A%8F%E6%97%B6%E9%9A%8F%E5%9C%B0%E8%AE%BF%E9%97%AE%E5%AE%B6%E9%87%8C%E7%9A%84nas/","title":"通过IPv6随时随地访问家里的NAS"},{"content":"前言 今天分享我自建NAS应用场景。首先介绍下什么是NAS？\nNAS，全称为网络附加存储（Network Attached Storage），是一种专门用于通过网络提供文件存储和访问的设备。和某网盘、某云盘的功能类似。\n为什么选择自建NAS？ 我从2015年开始接触NAS，但直到2019年才正式日常重度使用。原因：\n第一是当时手机存储空间告警照片资料急需备份； 第二是2020年儿子要出生，想多拍些照片视频做生活记录； 第三是随着年龄增长越来越怀旧，对过去的照片也越来越重视； 第四是现有网盘太难用，不是限速就是随意删我资料，app广告满屏飞舞，功能还限制（某网盘只让备份照片备份视频还得VIP），海外网盘可能会好点但是在国内使用条件苛刻。\n综上所述我选择自建私人网盘NAS。 我的NAS应用场景 主要应用场景：\n手机、电脑、相机照片视频备份管理 远程访问照片视频等资料 远程下载电影电视和远程播放 NAS融灾备份（主要是爱折腾，做个备份安心点） 我的解决方法 我NAS安装的群辉7.2系统，群辉系统功能非常丰富（PS：其实我也没用什么功能）。 以上场景对应解决方案：\n群辉synology photos 全套解决方案。手机端有photos mobile应用打开可实时备份浏览（PS：前提是以做好网络穿透或公网）电脑端web访问synology photo应用，手工上传历史照片或相机照片。系统会自动根据照片视频拍摄时间自动归类整理，人脸识别、主题识别自动归类。简直不要太方便 远程访问主要是解决公网访问难题。一开始我使用的frp内网穿透方式但很不稳定且速度慢（我的公网服务器带宽限制）播放视频和看PPT一样。现在使用IPv6方式，网速瓶颈是家里宽带的上行速率，虽然我家宽带上行是30M但相比某网盘已经非常快了。关于IPv6访问NAS方案这个后面在详细介绍。 远程下载需求是用群辉Download station。支持磁力和种子，偶尔下载电视和电影。这玩意儿当下火热资源速度不是问题，老旧的资源就自求多福吧。\n播放解决: 1）小米电视安装VLC播放器，通过本地samba共享播放 2）手机端VLC播放器，用DS file（群辉官方资源管理器）打开时选择VLC播放。因为我没有收藏电影的习惯看完就删所以没整什么电影分类海报墙。 双机冷备。主NAS和备NAS同网段，主NAS负责对外开服务，备NAS每天只开机一小时用于同步主NAS的资源。主NAS安装WebDAV Server 应用，备NAS安装Cloud Sync 应用，设置需要备份主NAS的路径和备NAS开关机时间。 以上方案不足之处：\nsynology photos不支持图片文字识别对照片搜索有些麻烦。在2万张照片里搜到我想要的那张照片，目前解决办法是1)提前对照片做好故事分类 2)靠脑袋回忆照片大概拍摄年份然后慢慢翻 synology photos照片归类错误。有时候照片视频非原图拍摄日期地点是错误自动归来就错误，使用synology photo修改很不方便，所以我找了专门修改照片信息的工具exiftools。另外可以利用iPhone照片编辑器修改好日期和位置再上传。 远程访问有时会失效。主要是IPv6兼容性问题，ipv4客户端无法访问IPv6服务器。有时手机的蜂窝网无法正常获取到IPv6地址从而导致无法访问NAS，此时可以开关下飞行模式重新注册下网络。通过测试是否支持IPv6测试是否正常获取到IPv6地址 老资源下载问题。Download station老资源或者其他一些资源无法下载，没啥好的解决方案，换其他下载器本地下载好了再上传吧。 总结 以上是我使用NAS的一些个人分享，希望对你有所帮助。 下一期做个IPv6公网访问的分享\n","date":"2024-06-01T01:17:29+08:00","image":"https://picsum.photos/800/600?r=2024-06-01T01:17:29+08:00","permalink":"https://zfj1441.com/p/%E6%88%91%E7%9A%84%E5%AE%B6%E7%94%A8nas%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF/","title":"我的家用NAS应用场景"},{"content":" 他没有直接发明很多东西，但是他用大师级的手法把理念、艺术和科技融合在一起，就创造了未来。他欣赏图形界面的威力，就以施乐公司无法做到的方式设计了Mac；他领会了把1 000首歌装进口袋的快乐，就以索尼尽其全部资产和传承都无法成就的方式创造了iPod。\n摘录来自 史蒂夫·乔布斯传 [美]沃尔特·艾萨克森\n引言 《乔布斯传》是沃尔特·艾萨克森（Walter Isaacson）撰写的史蒂夫·乔布斯（Steve Jobs）传记，详细讲述苹果公司创始人史蒂夫·乔布斯一生的传记。这本书不仅展示了乔布斯在科技领域的巨大成就，也揭示了他作为一个复杂人类的多面性。选择阅读这本书，是因为乔布斯作为科技创新的象征，对现代生活方式产生了深远影响，我希望通过这本书更深入地了解他的思维方式和人生轨迹。\n内容概要 传记分为多部分，分别讲述了乔布斯的早年生活、与斯蒂夫·沃兹尼亚克共同创立苹果公司、在苹果公司经历的起伏、创立NeXT和皮克斯动画工作室、以及重返苹果后的辉煌时期。书中详细描绘了乔布斯在产品设计、公司管理和市场营销方面的独特理念，以及他对完美主义和用户体验的执着追求。\n思考与启示 《乔布斯传》读完，这几天也来回想着读完书后对自己有什么启发和感悟。其中有两点影响比较大。 第一是敢于否认产品的设计。以前新接触一个东西总要琢磨半天怎么使用，看看说明书上网查查资料。但现在我敢说这是设计上的缺陷，做的不够简简洁。记得书里有这样一段描述：一个小孩在没有说明书没有任何人教的情况下自己就学会了使用ipad，这就是简洁的魅力。 第二是乔布斯做一体化的产品。从用户体验出发点做好产品，用户需要的好用的产品。\n","date":"2024-05-28T20:53:19+08:00","image":"https://picsum.photos/800/600?r=2024-05-28T20:53:19+08:00","permalink":"https://zfj1441.com/p/%E4%B9%94%E5%B8%83%E6%96%AF%E4%BC%A0%E8%AF%BB%E5%90%8E%E6%84%9F/","title":"《乔布斯传》读后感"},{"content":"前言 最近在网上看到一个非常有好玩技术，可以把彩色照片转成字符图片，先看看效果。\n原理 把图片中每个像素点转成灰度图（黑白电视的展现方式），然后根据每个像素的灰度值用不同的字符去替换。其中最大的问题是转字符后如何和原始图片尺寸保持一致，翻阅了很多资料才勉强看上去一致。\n关键步骤 从视频中提取图片 图片转字符文本 字符文本转字符图片 字符图片合成gif动态图 #!/usr/bin/python3 # coding: utf-8 import os import imageio.v2 as imageio import cv2 from PIL import Image, ImageDraw, ImageFont # ascii_char = list(\u0026#34;$@B%8\u0026amp;WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~\u0026lt;\u0026gt;i!lI;:,\\\u0026#34;^`\u0026#39;. \u0026#34;) ascii_char = list(\u0026#39;MNHQ$OC?7\u0026gt;!:-;. \u0026#39;) def get_char(r, g, b, alpha=256): if alpha == 0: return \u0026#39; \u0026#39; # 这是一个优化，将浮点运算转成整型运算 gray = (2126 * r + 7152 * g + 722 * b) / 10000 # 为什么是1.0 因为alpha、gray是整型运算的时候按整型算如果小于1的话按0算 char_idx = int((gray / (alpha + 1.0)) * len(ascii_char)) return ascii_char[char_idx] def text2image(content, width, height, filename): \u0026#39;\u0026#39;\u0026#39; 字符转图片 :param content: 字符数组 :param width: 输出的图片宽度 :param height: 输出的图片高度 :param filename: 输出的图片名称 :return: \u0026#39;\u0026#39;\u0026#39; image = Image.new(\u0026#34;RGB\u0026#34;, (width, height), (255, 255, 255)) canvas = ImageDraw.Draw(image) font = ImageFont.truetype(\u0026#39;JetBrainsMono-Thin.ttf\u0026#39;) fillcolor = \u0026#34;#000000\u0026#34; x, y = 0, 0 font_w, font_h = 6, 12 # 设置单个字符长款 for i in range(len(content)): if content[i] == \u0026#39;\\n\u0026#39;: x = -font_w y += font_h continue canvas.text((x, y), content[i], fill=fillcolor, font=font) x += font_w image.save(filename) def image2textimage(file_name=\u0026#34;test.jpg\u0026#34;, multiple=1.0, out_file_name=\u0026#39;output.txt\u0026#39;): \u0026#39;\u0026#39;\u0026#39; 将图片转成字符图片 :param file_name: :param multiple: 缩放比例 :param out_file_name: :return: \u0026#39;\u0026#39;\u0026#39; text = \u0026#39;\u0026#39; im = Image.open(file_name) width, height = int(im.size[0] * multiple), int(im.size[1] * multiple) width_text, height_text = int(width / 6), int(height / 12) # 原始图片长款等比例缩放 im = im.resize((width_text, height_text), Image.NEAREST) for i in range(height_text): for j in range(width_text): text += get_char(*im.getpixel((j, i))) text += \u0026#39;\\n\u0026#39; # with open(out_file_name, \u0026#39;w\u0026#39;) as f: # f.write(text) text2image(text, width, height, file_name + \u0026#39;_txt.png\u0026#39;) return file_name + \u0026#39;_txt.png\u0026#39; def video2images(source_video, start_time, end_time, interval=1.0): \u0026#39;\u0026#39;\u0026#39; 将视频转保存为图片s :param source_video: 视频文件 :param start_time: 开始时间/s :param end_time: 结束时间/s :param interval: 时间间隔/s :return: \u0026#39;\u0026#39;\u0026#39; # 创建零时目录 tmppath = os.path.join(os.getcwd(), \u0026#39;tmp\u0026#39;) if not os.path.exists(tmppath): os.mkdir(tmppath) cap = cv2.VideoCapture(source_video) if not cap.isOpened(): print(\u0026#34;Error: Could not open video.\u0026#34;) return [] else: fps = cap.get(5) # 帧速率 frame_number = cap.get(7) # 视频文件的帧数 duration = frame_number / fps # 视频总帧数/帧速率 是时间/秒 print(\u0026#34;总时长[%.2f]秒,开始时间[%.2f]秒结束时间[%.2f]秒\u0026#34; % (duration, start_time, end_time)) if start_time \u0026gt; duration or end_time \u0026gt; duration: print(\u0026#34;Error: 截取时间断异常.总时长[%d]秒,开始时间[%d]秒结束时间[%d]秒\u0026#34; % (duration, start_time, end_time)) return [] start_frame = int(fps * float(start_time)) end_frame = int(fps * float(end_time)) interval_frame = int(fps * float(interval)) print(\u0026#34;总帧数[%d],帧率[%d],开始帧数[%d]，结束帧数[%d]， 间隔[%d]\u0026#34; % (frame_number, fps, start_frame, end_frame, interval_frame)) num = 0 images = [] while True: success, frame = cap.read() if success: if int(start_frame) \u0026lt;= int(num) \u0026lt;= int(end_frame): if success and int(num) % int(interval_frame) == 0: image_name = os.path.join(tmppath, f\u0026#34;frame_{num}.png\u0026#34;) cv2.imwrite(image_name, frame) images.append(image_name) num += 1 if num \u0026gt; frame_number: break cap.release() return images return texts def image2gif(textimages, output, fps=2): images=[] for pic in textimages: images.append(imageio.imread(pic)) imageio.mimsave(output, images, format=\u0026#39;GIF\u0026#39;, fps=fps, loop=0) def fontText(path=\u0026#39;\u0026#39;): \u0026#39;\u0026#39;\u0026#39; 查看文本字符宽度和高度 :param path: :return: \u0026#39;\u0026#39;\u0026#39; font = ImageFont.truetype(path) for t in ascii_char: fbox = font.getbbox(t) print(t, fbox[2]-fbox[0], fbox[3]-fbox[1]) if __name__ == \u0026#39;__main__\u0026#39;: images = video2images(\u0026#39;庆余年2.mov\u0026#39;, start_time=3, end_time=21) textImages = [] for im in images: textImages.append(image2textimage(im)) image2gif(textImages, \u0026#39;output.gif\u0026#39;) 完成项目内容见附件：源码附件\n参考 使用python中的imageio生成gif动态图\nPython写实用小工具-实现图片转字符画\n使用Python完成曾火爆全网的图片转符号图片、GIF图像转字符GIF动画操作\n","date":"2024-05-25T15:56:49+08:00","image":"https://picsum.photos/800/600?r=2024-05-25T23:18:49+08:00","permalink":"https://zfj1441.com/p/python%E5%AE%9E%E7%8E%B0%E8%A7%86%E9%A2%91%E8%BD%ACgif%E5%AD%97%E7%AC%A6%E5%8A%A8%E6%80%81%E5%9B%BE/","title":"Python实现视频转gif字符动态图"},{"content":"前言 最近自测个项目，为了方便发起交易写了几个shell脚本。期间也是发现太久没写了很多基础的都忘了,写个博客记录下常用操作。\n常用 list mylist=(1 2 3) echo ${#mylist[@]} 3 echo ${mylist[1]} 2 echo ${mylist[-2]} 2 echo ${mylist[@]} 1 2 3 echo ${mylist[*]} 1 2 3 for i in ${mylist[@]};do echo ${i} done if判断 if [[ $1 = \u0026#39;tomcat\u0026#39; ]]; then echo \u0026#34;Input is tomcat\u0026#34; elif [[ $1 = \u0026#39;redis\u0026#39; ]] || [[ $1 = \u0026#39;zookeeper\u0026#39; ]]; then echo \u0026#34;Input is $1\u0026#34; else echo \u0026#34;Input Is Error.\u0026#34; fi # 文件测试运算符： -f FILE: 文件 FILE 存在且是一个常规文件。 -d DIR: 文件 DIR 存在且是一个目录。 -e FILE: 文件 FILE 存在。 -s FILE: 文件 FILE 存在且不为空。 -r FILE: 文件 FILE 存在且可读。 -w FILE: 文件 FILE 存在且可写。 -x FILE: 文件 FILE 存在且可执行 # 字符串测试运算符： STRING1 = STRING2: 字符串 STRING1 和 STRING2 相等。 STRING1 != STRING2: 字符串 STRING1 和 STRING2 不相等。 -z STRING: 字符串 STRING 的长度为零。 -n STRING: 字符串 STRING 的长度非零 # 整数比较运算符： INTEGER1 -eq INTEGER2: 整数 INTEGER1 等于 INTEGER2。 INTEGER1 -ne INTEGER2: 整数 INTEGER1 不等于 INTEGER2。 INTEGER1 -gt INTEGER2: 整数 INTEGER1 大于 INTEGER2。 INTEGER1 -ge INTEGER2: 整数 INTEGER1 大于等于 INTEGER2。 INTEGER1 -lt INTEGER2: 整数 INTEGER1 小于 INTEGER2。 INTEGER1 -le INTEGER2: 整数 INTEGER1 小于等于 INTEGER2 # 逻辑运算符： ! EXPRESSION: 条件 EXPRESSION 不成立。 EXPRESSION1 -a EXPRESSION2: 条件 EXPRESSION1 和 EXPRESSION2 都成立（逻辑与）。 EXPRESSION1 -o EXPRESSION2: 条件 EXPRESSION1 和 EXPRESSION2 有一个成立（逻辑或） for循环 for((i=1;i\u0026lt;=10;i++)); do echo $(expr $i \\* 3 + 1); done for i in $(seq 1 10) do echo $(expr $i \\* 3 + 1); done for i in {1..10} do echo $(expr $i \\* 3 + 1); done 字符串截取 STR=\u0026#34;1234567890asdfghjkl\u0026#34; echo \u0026#34;first character ${STR:0:1}\u0026#34; echo \u0026#34;The three characters ${STR:0:3}\u0026#34; echo \u0026#34;第四个字符开始显示:${STR:3}\u0026#34; echo \u0026#34;显示第4到6个字符:${STR:3:3}\u0026#34; echo \u0026#34;显示最后1个字符:${STR:0-1}\u0026#34; echo \u0026#34;显示最后3个字符:${STR:0-3}\u0026#34; echo \u0026#34;从右边数第7个，取3个字符${STR:0-7:3}\u0026#34; STR=abc123bcd456123abc777 echo ${STR#*123} #bcd456123abc777 echo ${STR##*123} #abc777 echo ${STR%123*} #abc123bcd456 echo ${STR%%123*} #abc date +%Y-%m-%d.%H:%M:%S.%N|cut -c 1-23 //截取前23位，为毫秒 2019-04-25.00:49:31.100 date +%Y-%m-%d.%H:%M:%S.%N|cut -c 1-26 //截取前26位，为微秒 2019-04-25.00:51:15.191520 echo $STR|grep -o \u0026#34;ui.*h\u0026#34; 字符串替换 s1=12345678 s2=abcdefghijk echo ${s1/78/${s2}} # 123456abcdefghijk 替换首次出现的78 echo ${s1//78/${s2}} # 123456abcdefghijk 替换所有78 echo ${s1/#78/${s2}} # 12345678 替换开头 echo ${s1/%78/${s2}} # 123456abcdefghijk 替换结尾 echo $s1 | tr \u0026#39;78\u0026#39; $s2 # 123456ab echo $s1 | sed \u0026#34;s/78/$s2/g\u0026#34; # 123456abcdefghijk 字符串包含 s1=12345678 result=`echo $s1|grep \u0026#34;5678\u0026#34;` if [[ \u0026#34;$result\u0026#34; != \u0026#34;\u0026#34; ]];then echo \u0026#34;包含\u0026#34; else echo \u0026#34;不包含\u0026#34; fi 参考 笔记 | Bash 中 if 判断选项\nshell中常用的变量处理 Shell中for循环的几个常用写法\n","date":"2024-05-23T23:03:56+08:00","image":"https://picsum.photos/800/600?r=2024-05-23T23:03:56+08:00","permalink":"https://zfj1441.com/p/%E6%97%A5%E5%B8%B8shell%E5%88%86%E4%BA%AB/","title":"日常shell分享"},{"content":"前言 今天整理电脑资料，无意间翻出来一道CTF题。考题内容是变种base64加解密，解题程序是当时做题是从互联网收集，个博客做个记录，顺便回顾下base64加解密原理。\nbase64加解密原理 base64字符的组成部分 A-Z、a-z、0-9、+、/\n26 + 26 + 10 + 2 = 64 Base64表\n加密原理 先将每三个字符分离，最后有可能剩0个或者1个或者2个字符。 整三个：将三个字符转换为ascii二进制码，得到24bit(3*8bit)，然后再按顺序分为4份(每6bit为一份)。最后，将这四份二进制转换为4份十进制，再按照Base64字符表转为4个字符。 分离后剩1个字符：将这一个字符转换为ascii二进制码，先切一个6bit还剩2bit，再将这2bit后面补4个0。最后，将这两份二进制转换为2份十进制，再按照Base64字符表转为2个字符，后面再补两个等于号(这里两个等号打不出来)。 分离后剩2个字符：将这一个字符转换为ascii二进制码，先切两个6bit还剩4bit，再将这2bit后面补2个0。最后，将这三份二进制转换为3份十进制，再按照Base64字符表转为3个字符，后面再补一个等于号(=)。 Base64对于中文的不同编码可能会出现不同的结果，具体要看中文用的是什么编码。 以字符串\u0026quot;php\u0026quot;为例 对应ASCII： 01110000 01101000 01110000\n每6位分割： 011100 000110 100001 110000\n对应10进制值： 28 6 33 48\nBase64对应的字符：c G h w\n结果： php = cGhw（base64） 解密 看懂加密，解密就是加密的逆向，这里要强调的是：Base64解密也是靠着Base64表解密的，如果碰到不在Base64表的字符(空格，\u0026lt;,\u0026gt;,等等)，将会跳过这些字符，仅将在表内的字符组成一个新的字符串进行解码。 base64变种 base64变种主要是围绕base64码表，比如对码表做凯撒加密处理，或是题目给一段自定义64码表\nbase64加解密程序 # python版本 # coding:utf-8 #s = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#34; s = \u0026#34;uL9BG2i1po7z6MnCQf8rvDxJt5Ahw3kjlKWPesqy+EdZbc/aS0HOVXYmNgIRTFU4\u0026#34; def My_base64_encode(inputs): # 将字符串转化为2进制 bin_str = [] for i in inputs: x = str(bin(ord(i))).replace(\u0026#39;0b\u0026#39;, \u0026#39;\u0026#39;) bin_str.append(\u0026#39;{:0\u0026gt;8}\u0026#39;.format(x)) #print(bin_str) # 输出的字符串 outputs = \u0026#34;\u0026#34; # 不够三倍数，需补齐的次数 nums = 0 while bin_str: #每次取三个字符的二进制 temp_list = bin_str[:3] if(len(temp_list) != 3): nums = 3 - len(temp_list) while len(temp_list) \u0026lt; 3: temp_list += [\u0026#39;0\u0026#39; * 8] temp_str = \u0026#34;\u0026#34;.join(temp_list) #print(temp_str) # 将三个8字节的二进制转换为4个十进制 temp_str_list = [] for i in range(0,4): temp_str_list.append(int(temp_str[i*6:(i+1)*6],2)) #print(temp_str_list) if nums: temp_str_list = temp_str_list[0:4 - nums] for i in temp_str_list: outputs += s[i] bin_str = bin_str[3:] outputs += nums * \u0026#39;=\u0026#39; print(\u0026#34;Encrypted String:\\n%s \u0026#34;%outputs) def My_base64_decode(inputs): # 将字符串转化为2进制 bin_str = [] for i in inputs: if i != \u0026#39;=\u0026#39;: x = str(bin(s.index(i))).replace(\u0026#39;0b\u0026#39;, \u0026#39;\u0026#39;) bin_str.append(\u0026#39;{:0\u0026gt;6}\u0026#39;.format(x)) #print(bin_str) # 输出的字符串 outputs = \u0026#34;\u0026#34; nums = inputs.count(\u0026#39;=\u0026#39;) while bin_str: temp_list = bin_str[:4] temp_str = \u0026#34;\u0026#34;.join(temp_list) #print(temp_str) # 补足8位字节 if(len(temp_str) % 8 != 0): temp_str = temp_str[0:-1 * nums * 2] # 将四个6字节的二进制转换为三个字符 for i in range(0,int(len(temp_str) / 8)): outputs += chr(int(temp_str[i*8:(i+1)*8],2)) bin_str = bin_str[4:] print(\u0026#34;Decrypted String:\\n%s \u0026#34;%outputs) print() print(\u0026#34; *************************************\u0026#34;) print(\u0026#34; * (1)encode (2)decode *\u0026#34;) print(\u0026#34; *************************************\u0026#34;) print() num = input(\u0026#34;Please select the operation you want to perform:\\n\u0026#34;) if(num == \u0026#34;1\u0026#34;): input_str = input(\u0026#34;Please enter a string that needs to be encrypted: \\n\u0026#34;) My_base64_encode(input_str) else: input_str = input(\u0026#34;Please enter a string that needs to be decrypted: \\n\u0026#34;) My_base64_decode(input_str) /*C语言版本*/ #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; // 全局常量定义 // const char * base64char = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#34;; const char * base64char = \u0026#34;uL9BG2i1po7z6MnCQf8rvDxJt5Ahw3kjlKWPesqy+EdZbc/aS0HOVXYmNgIRTFU4\u0026#34;; const char padding_char = \u0026#39;=\u0026#39;; /*编码代码 * const unsigned char * sourcedata， 源数组 * char * base64 ，码字保存 */ int base64_encode(const unsigned char * sourcedata, char * base64) { int i = 0, j = 0; unsigned char trans_index = 0; // 索引是8位，但是高两位都为0 const int datalength = strlen((const char*)sourcedata); for (; i \u0026lt; datalength; i += 3){ // 每三个一组，进行编码 // 要编码的数字的第一个 trans_index = ((sourcedata[i] \u0026gt;\u0026gt; 2) \u0026amp; 0x3f); base64[j++] = base64char[(int)trans_index]; // 第二个 trans_index = ((sourcedata[i] \u0026lt;\u0026lt; 4) \u0026amp; 0x30); if (i + 1 \u0026lt; datalength){ trans_index |= ((sourcedata[i + 1] \u0026gt;\u0026gt; 4) \u0026amp; 0x0f); base64[j++] = base64char[(int)trans_index]; } else{ base64[j++] = base64char[(int)trans_index]; base64[j++] = padding_char; base64[j++] = padding_char; break; // 超出总长度，可以直接break } // 第三个 trans_index = ((sourcedata[i + 1] \u0026lt;\u0026lt; 2) \u0026amp; 0x3c); if (i + 2 \u0026lt; datalength){ // 有的话需要编码2个 trans_index |= ((sourcedata[i + 2] \u0026gt;\u0026gt; 6) \u0026amp; 0x03); base64[j++] = base64char[(int)trans_index]; trans_index = sourcedata[i + 2] \u0026amp; 0x3f; base64[j++] = base64char[(int)trans_index]; } else{ base64[j++] = base64char[(int)trans_index]; base64[j++] = padding_char; break; } } base64[j] = \u0026#39;\\0\u0026#39;; return 0; } /** 在字符串中查询特定字符位置索引 * const char *str ，字符串 * char c，要查找的字符 */ int num_strchr(const char *str, char c) // { const char *pindex = strchr(str, c); if (NULL == pindex){ return -1; } return pindex - str; } /* 解码 * const char * base64 码字 * unsigned char * dedata， 解码恢复的数据 */ int base64_decode(const char * base64, unsigned char * dedata) { int i = 0, j = 0; int trans[4] = { 0, 0, 0, 0 }; for (; base64[i] != \u0026#39;\\0\u0026#39;; i += 4){ // 每四个一组，译码成三个字符 trans[0] = num_strchr(base64char, base64[i]); trans[1] = num_strchr(base64char, base64[i + 1]); // 1/3 dedata[j++] = ((trans[0] \u0026lt;\u0026lt; 2) \u0026amp; 0xfc) | ((trans[1] \u0026gt;\u0026gt; 4) \u0026amp; 0x03); if (base64[i + 2] == \u0026#39;=\u0026#39;){ continue; } else{ trans[2] = num_strchr(base64char, base64[i + 2]); } // 2/3 dedata[j++] = ((trans[1] \u0026lt;\u0026lt; 4) \u0026amp; 0xf0) | ((trans[2] \u0026gt;\u0026gt; 2) \u0026amp; 0x0f); if (base64[i + 3] == \u0026#39;=\u0026#39;){ continue; } else{ trans[3] = num_strchr(base64char, base64[i + 3]); } // 3/3 dedata[j++] = ((trans[2] \u0026lt;\u0026lt; 6) \u0026amp; 0xc0) | (trans[3] \u0026amp; 0x3f); } dedata[j] = \u0026#39;\\0\u0026#39;; return 0; } // 测试 int main() { const unsigned char str[] = \u0026#34;a45rbcd\u0026#34;; const unsigned char *sourcedata = str; char base64[512]; base64_encode(str, base64); printf(\u0026#34;编码：%s\\n\u0026#34;, base64); char dedata[128]; base64_decode(base64, (unsigned char*)dedata); printf(\u0026#34;译码：%s\u0026#34;, dedata); getchar(); return 0; } 参考 Base64加密解密原理\n","date":"2024-05-12T01:17:29+08:00","image":"https://picsum.photos/800/600?r=2024-05-12T01:17:29+08:00","permalink":"https://zfj1441.com/p/base64%E5%8A%A0%E8%A7%A3%E5%AF%86%E5%8E%9F%E7%90%86%E5%8F%8Abase64%E5%8F%98%E7%A7%8D/","title":"base64加解密原理及base64变种"},{"content":"前言 项目开发经常需要临时安装个数据库，安装工具，搭建一套测试环境什么的。直接在服务器上安装，会有很多插件依赖库慢慢的拖慢服务器。使用Docker安装是个非常不错的主意。以下是我整理自用的Docker项目，供大家参考学习。\nDocker项目 docker安装postgres数据库 mkdir -p /root/waline/postgresql/data docker run -d \\ -v /root/waline/postgresql/data:/var/lib/postgresql/data \\ -e POSTGRES_PASSWORD=postgres \\ -p 127.0.0.1:5432:5432 \\ --name postgres \\ postgres #查看容器虚拟IP地址 docker inspect postgres |grep IPAddress #pg执行SQL文件 docker exec -it postgres /bin/bash psql -U postgres -d postgres -h 172.17.0.3 -p 5432 -f xx.sql docker安装ssr服务器 docker run -p 51348:51348 \\ --restart=always \\ --name=SSR \\ -e PASSWORD=passw0rd \\ -e METHOD=chacha20-ietf \\ -e PROTOCOL=auth_sha1_v4 \\ -e OBFS=tls1.2_ticket_auth \\ -d breakwa11/shadowsocksr docker安装waline评论系统 docker run -d \\ -e PG_DB=postgres \\ -e PG_USER=postgres \\ -e PG_PASSWORD=postgres \\ -e PG_HOST=172.17.0.3 \\ -e PG_PORT=5432 \\ -e TZ=Asia/Shanghai \\ -p 127.0.0.1:8360:8360 \\ --name=waline \\ lizheming/waline:1.31.5 # 基于sqlite的waline mkdir -p /root/waline Cd /root/waline \u0026amp;\u0026amp; wget https://github.com/walinejs/waline/blob/main/assets/waline.sqlite docker run -d \\ -v /root/waline:/app/waline \\ -e SQLITE_PATH=/app/waline \\ -e JWT_TOKEN=xxxxxxx \\ -e TZ=Asia/Shanghai \\ -p 127.0.0.1:8360:8360 \\ --name=waline \\ lizheming/waline docker安装it-tools工具集合 docker run -d \\ --name it-tools --restart unless-stopped \\ -v /root/it-tools/html:/usr/share/nginx/html \\ -p 127.0.0.1:6005:80 \\ ghcr.io/corentinth/it-tools:latest # nginx配置反向代理 server { if ($request_method !~* GET|POST) { return 403; } listen 443 ssl; ssl_certificate /home/xiaoming/demo.crt; #证书 ssl_certificate_key /home/xiaoming/demo.key; #密钥 charset utf-8; #it-tools工具集 location /it-tools/ { rewrite ^/it-tools/(.*) /$1 break; proxy_pass http://127.0.0.1:6005; } } docker安装homeassistant mkdir -p /root/config docker run -d -p 8123:8123 --restart=always \\ --name=homeAssistant.2023.6 \\ -v /root/config:/config \\ -e TZ=Asia/Shanghai \\ --privileged=true \\ --net=host \\ homeassistant/raspberrypi3-homeassistant:stable docker安装mqtt服务器 mkdir -p /root/mosquitto/config mkdir -p /root/mosquitto/data mkdir -p /root/mosquitto/log vi /root/mosquitto/config/mosquitto.conf persistence true persistence_location /mosquitto/data log_dest file /mosquitto/log/mosquitto.log bind_address 0.0.0.0 port 1883 chmod -R 755 /root/mosquitto docker run -d \\ --net=\u0026#34;host\u0026#34; \\ --name=mosquitto \\ --restart=always \\ --privileged \\ -e TZ=\u0026#34;Asia/Shanghai\u0026#34; \\ -p 1883:1883 \\ -p 9001:9001 \\ -v /root/mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \\ -v /root/mosquitto/data:/mosquitto/data \\ -v /root/mosquitto/log:/mosquitto/log \\ eclipse-mosquitto 1）、配置文件添加以下配置 # 关闭匿名模式 allow_anonymous false # 指定密码文件 password_file /mosquitto/config/pwfile.conf 2）、进入容器 docker exec -it mosquitto sh 3）、生成密码(在容器内执行) #对于passworf_file，可以复制一份模板，或者创建一个空文件 touch /mosquitto/config/pwfile.conf chmod -R 755 /mosquitto/config/pwfile.conf # 使用mosquitto_passwd命令创建用户，第一个lxy是用户名，第二个lxy是密码 mosquitto_passwd -b /mosquitto/config/pwfile.conf hass hass doccker安装博客typecho mkdir /root/typecho #chmod -R user:user /root/typecho #依稀记得要给权限 docker run \\ --name typecho \\ -p 6002:80 \\ -v /root/typecho:/app \\ -e TYPECHO_SITE_URL=https://www.demo.com \\ -d joyqi/typecho:nightly-php7.4-apache docker安装chrome浏览器(实际就是linux系统默认启动chrome) docker run -d \\ --name chrome \\ -e LANG=en_US.UTF-8 \\ -e LANGUAGE=en_US.UTF-8 \\ -e LC_ALL=C.UTF-8 \\ -e TZ=Asia/Shanghai \\ -e SCREEN_RESOLUTION=1280x720 \\ -p 8083:8083 \\ -p 5900:5900 \\ oldiy/chrome-novnc:latest docker安装alist挂在网盘 mkdir -p /root/alist/data docker run -d \\ --restart=always \\ -v /root/alist/data:/opt/alist/data \\ -p 127.0.0.1:5244:5244 \\ -e PUID=0 \\ -e PGID=0 \\ -e UMASK=022 \\ --name=\u0026#34;alist\u0026#34; \\ xhofe/alist:latest-ffmpeg # 随机生成一个密码 docker exec -it alist ./alist admin random # 手动设置一个密码,`NEW_PASSWORD`是指你需要设置的密码 docker exec -it alist ./alist admin set NEW_PASSWORD # 修改/root/alist/data/config.json中 \u0026#34;site_url\u0026#34;: \u0026#34;pan\u0026#34; # 修改nginx端口转发配置 location /pan/ { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-NginX-Proxy true; rewrite ^/(.*) /$1 break; proxy_pass http://127.0.0.1:5244; } 总结 先就这些吧，以后有新内容加添加。希望对各位有帮助。\n","date":"2024-05-07T22:08:44+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T22:08:44+08:00","permalink":"https://zfj1441.com/p/docker%E5%B8%B8%E7%94%A8%E9%95%9C%E5%83%8F%E6%95%B4%E7%90%86/","title":"Docker常用镜像整理"},{"content":"前言 去年海外VPS上使用hexo+butterfly构建博客系统，体验还不错，文档、主题资源都很全。但hexo是nodejs开发，因为我对nodejs一窍不通当时搭建时倒腾了半天。所以这次重建博客系统我计划使用hugo。Hugo是由Go语言实现的静态网站生成器，因为是golang开发编译速度很快且hugo只有一个执行程序很简洁。\n博客系统安装 博客搭建基于centos7系统,以下是我使用hugo-theme-stack主题调整修改的模板。有需要可直接食用，不用过多优化配置。\n# 升级libstdc++ strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX wget http://mirror.ghettoforge.org/distributions/gf/el/7/gf/x86_64/gf-release-7-12.gf.el7.noarch.rpm rpm -Uvh gf-release-7-12.gf.el7.noarch.rpm yum install gcc10-libstdc++ rpm -ql gcc10-libstdc++.x86_64 cp /opt/gcc-10.2.1/usr/lib64/libstdc++.so.6.0.28 /usr/lib64/ cd /usr/lib64 mv libstdc++.so.6 libstdc++.so.6.20240422 ln -s libstdc++.so.6.0.28 libstdc++.so.6 strings /usr/lib64/libstdc++.so.6 | grep GLIBCXX # 安装git yum install git # 安装hugo命令(一定要安装extended版本) wget https://github.com/gohugoio/hugo/releases/download/v0.125.5/hugo_extended_0.125.5_linux-amd64.tar.gz tar -zxvf hugo_extended_0.125.5_linux-amd64.tar.gz mkdir -p ~/bin \u0026amp;\u0026amp; mv hugo ~/bin hugo version # 解压博客模板(见附件hugo-theme-stack主题并部分修改) mkdir -p ~/www tar -zxvf site.20240501.tar.gz -C www # 部署 cd site hugo #生成静态页面，执行命令后会生成public目录，该目录就是所有网站内容 #nginx.conf相关配置 user xiaoming;#注意nginx权限 server { if ($request_method !~* GET|POST) { return 403; } listen 80; server_name www.demo.com; #80端口自动跳转443 rewrite ^(.*)$ https://${server_name}$1 permanent; } server { if ($request_method !~* GET|POST) { return 403; } listen 443 ssl; ssl_certificate /home/xiaoming/etc/www.demo.crt; #网站证书 ssl_certificate_key /home/xiaoming/etc/www.demo.key; #网站私钥 access_log /home/xiaoming/log/nginx/service.log main; charset utf-8; location / { root /home/xiaoming/www/site/public; #hugo生成静态网站路径 index index.html index.htm; } } 文件结构 ── site ├── archetypes\t#文章内容模板 ├── assets\t#存放图标、图片、js(ts)脚本文件，css(scss)样式文件； ├── content\t#文章、页面、分类页面等文件 │ ├── categories\t#分类页面目录。 │ ├── page\t#归档、友链、关于之类页面的目录。 │ └── post\t#存放博客文件的目录。 ├── layouts\t#网页布局文件 ├── public\t#hugo编译后的网站文件夹； ├── resources ├── static #全局图片等静态文件 │ └── img │ └── file └── themes 日常写作 创建文章 单文件模式，一个文件就是一片内容（优点是简洁，一篇文章对应一个md文件。缺点就是附件难管理，只能放在全局静态资源下site/static/img，引用时![图名](/img/image.png)）\nhugo new post/xxxx.md 文件夹模式,一个目录就是一片内容（与单文件模式相反，但是文章附件可以和内容放在一起方便后期维护管理,引用时直接用图片名就可以![图名](image.png)）\nhugo new post/xxxx/index.md 修改md文件draft: false 执行hugo重新生成public内容 site.demo\n参考 Centos更新lib库[踩坑记录]\n第三夏尔-博客建站\n（3）Stack主题的自定义\nhugo stack 主题美化\n基于 Hugo 的网站搭建日志 01\n","date":"2024-05-03T00:35:17-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T00:35:17-04:00","permalink":"https://zfj1441.com/p/%E4%BA%94%E4%B8%80%E5%81%87%E6%9C%9F%E9%87%8D%E6%95%B4%E6%88%91%E7%9A%84%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/","title":"五一假期，重整我的个人博客"},{"content":"背景 去年购买BWH的海外VPS服务器到期，不得不说BWH的服务器真是贵，所以最近新换了个服务商的VPS服务器。在这几分享下新购服务器后基本操作，以下centos7操作系统为例\n具体操作 1.使用ROOT创建普通用户\nuseradd xiaoming passwd xiaoming 2.修改sshd监听端口并禁止ROOT用户直接登录\nvi /etc/ssh/sshd_config PermitRootLogin no Port 2222 # SELinux的策略 yum -y install policycoreutils-python semanage port -a -t ssh_port_t -p tcp 2222 # 防火墙放通 2222端口 firewall-cmd --permanent --zone=public --add-port=2222/tcp firewall-cmd --reload # 重启ssh服务 systemctrl restart sshd 3.使用iptables替换firewall\n# 禁用firwall systemctl stop firewalld.service systemctl disable firewalld.service # 安装iptables yum install iptables-services -y systemctl enable iptables systemctl start iptables #编辑防火墙配置文件 vim /etc/sysconfig/iptables *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT -A INPUT -p tcp -m tcp --dport 80 -j ACCEPT -A INPUT -p tcp -m tcp --dport 2222 -j ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT # 重启iptables systemctl restart iptables 4.关闭非业务端口服务关闭(25端口)\nsystemctl stop postfix systemctl disable postfix 5.安装nginx\nrpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm yum install nginx 6.安装docker\nyum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin # 启动Docker systemctl start docker # 停止Docker systemctl stop docker # 重启 systemctl restart docker # 设置开机自启 systemctl enable docker # 执行docker ps命令，如果不报错，说明安装启动成功 docker ps ","date":"2024-04-16T08:30:14-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T08:30:14-04:00","permalink":"https://zfj1441.com/p/%E5%88%86%E4%BA%AB%E6%96%B0%E8%B4%ADvps%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C/","title":"分享新购VPS服务器基本操作"},{"content":"背景 计划小米平板2上墙，所以用python写个windows屏幕和电池监控程序，方便实现自动充电。\n源码简单描述： 使用pywin32库注册监听屏幕、电池变化事件，然后在回调函数里组织数据上传到MQTT服务器。源代码全部开放有能力的自己新加功能。\n效果展示 使用方法： 配置config.ini中的mqtt服务器信息 上传config.ini screenMonitor.exe startServer.bat 到平板并放在同一路径下 双击startServer.bat 上传xiaomi_pad.yaml到homeassistant服务器config/packages目录下 重启homeassistant 或 在homeassistant\u0026rsquo;开发者工具\u0026rsquo;-\u0026gt;\u0026lsquo;配置重载\u0026rsquo;-\u0026gt;\u0026lsquo;手动配置的MQTT实体\u0026rsquo; 配置homeassistant自动化 附件： 123盘 https://www.123pan.com/s/zrRrVv-HYnJ.html 提取码:ThNE\nhttps://image.vitshare.cn/blog/content/post/分享windows平板屏幕电量监控程序，python全套源码开放/源码.zip\n","date":"2024-04-15T00:00:00Z","image":"https://picsum.photos/800/600?r=2024-4-15T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%88%86%E4%BA%ABwindows%E5%B9%B3%E6%9D%BF%E5%B1%8F%E5%B9%95%E7%94%B5%E9%87%8F%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8Fpython%E5%85%A8%E5%A5%97%E6%BA%90%E7%A0%81%E5%BC%80%E6%94%BE/","title":"分享windows平板屏幕电量监控程序，python全套源码开放"},{"content":"背景 有那么一天携家人回乡下玩耍，家人说家里的wifi不好用，一楼的卫生间、厨房、卧室wifi信号特别弱。我大概看了下路由器是电信免费配送的放在二楼客厅，wifi信号到厨房卫生间中间至少经过3度墙，而且家里的墙还是贼厚的那种，信号弱也正常。之后便有了扩展wifi的想法。回来后了解wifi扩展方案：有AC+AP、无线桥接、mesh等等。AC+AP方案不仅投入大且还得需要一定技术，我这网工小白算了。无线桥接方案之前整过效果一般，所以这次使用mesh试试效果。\n需求 低成本把WiFi扩展，能做到全屋覆盖wifi 效果立竿见影，至少蹲厕所刷视频不卡顿 方案 咸鱼采购2个斐讯K3，同时要求卖家刷好梅林固件。成本大概2张毛子 梅林固件AiMesh配置。 1）主路由器在“系统管理“-\u0026gt;\u0026ldquo;操作模式\u0026quot;中选择\u0026quot;无线路由器模式/AiMesh路由器模式（默认）\u0026quot;。 2）主路由器打开AiMesh功能。 3）子路由器在“系统管理“-\u0026gt;\u0026ldquo;操作模式\u0026quot;中选择\u0026quot;AiMesh节点\u0026rdquo;。 4）子路由器的wan口 和 主路由器的lan口用网线连接 5）主路由器进入AiMesh菜单搜索到子节点，等子路由器重启整个配置就完成。 路由器位置摆放。主路由器继续放在二楼客厅处，子路由器放在厨房卫生间中间，同时避免穿过多的强。 使用体验 加了两个路由器一楼wifi信号都已经覆盖全了，但使用下来有时候还是有问题 1）手机有wifi信号但是视频依旧卡顿的情况。2）手机连接wifi5，稍微走动下wifi就不行了。 之后在主路由器上强制关闭了5G，只用使用2.4G，虽然速率降低了但穿墙好，同时没有5G和2.4G切换系统也稳定很多。\n","date":"2024-02-01T07:41:59-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T07:41:59-04:00","permalink":"https://zfj1441.com/p/%E8%AE%B0%E5%BD%95%E6%89%A9%E5%B1%95%E5%AE%B6%E9%87%8Cwifi%E8%A6%86%E7%9B%96/","title":"记录扩展家里wifi覆盖"},{"content":"故事 周末一个人在家，打开电视准备随便看看，点开后我发现除了新上映的资源一些过时的资源也同样需要VIP；后来索性听点歌得了，但没听几首又出现试听完整歌曲内容还得是VIP。此时真好无奈，手握遥控器的我不知该往哪里点好，只好默默选择关机键。第二天，看着黑屏的电视默默的挂着，那今天就开始折腾它。\n我的电视需求 CCTV频道：看看新闻联播，假装关心下国家大事。 爱奇艺、芒果TV：偶尔看下最新的节目综艺。 本地播放器：播放NAS中高清小电影。 音乐播放器：小曲哼起来。 处理方案 换桌面：小米桌面软件广告是真的多，后面尝试ATV桌面、当贝桌面。 本地播放器：其实小米播放器我还是挺中意的，功能满足我的需求同时简介。但是换了桌面后就用不了（没有图标），没办法只好换KODI，跨平台开源软件，功能很全就是设置有点难，琢磨了两天才满足需求本地播放、音乐播放。 CCTV、爱艺奇、芒果TV 安装对应软件就OK了。 抢救方案 按小米电视去广告及更换第三方桌面操作方法 中的脚本跑了下，结果三方桌面没装上倒是把小米桌面给卸载了，导致重启进不来系统。后面参考小米电视不小心把系统桌面给删，快速修复！ 救活。最后修改下脚本重新刷 。\n2、蓝牙遥控器 ① 电视： a. 先将电视断电； b. 按住蓝牙遥控器的“确认键” “返回键”不松手； c. 插入电视电源（无需点击电源键），直到进入Recovery 模式再松手； d. 若该方法无法进入，可在电视关机后，同时按住“确认键” “返回键”不松手，点击电视开机键，待电视进入Recovery 模式后将遥控器按键松开即可；\n小米电视去广告bat.zip\n小米电视去广告bat.zip(123云盘)\n参考 小米电视去开机广告，开机默认当贝桌面+电视系统精简保姆级教程（小米通用）\n小米电视去广告及更换第三方桌面操作方法\n小米电视不小心把系统桌面给删，快速修复！\n当贝桌面_4.1.6_T,可代替原电视桌面,去更新,养老版\n","date":"2023-09-17T07:37:16-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T07:37:16-04:00","permalink":"https://zfj1441.com/p/%E5%B0%8F%E7%B1%B3%E7%94%B5%E8%A7%864%E6%8D%A2%E6%A1%8C%E9%9D%A2%E5%8E%BB%E5%B9%BF%E5%91%8A/","title":"小米电视4换桌面去广告"},{"content":"前言 上篇说到exiftool改拍摄时间、地理位置等信息，这篇具体讲下如果修改照片GPS。之所以要详细说是因为自己在修改GPS中发现不管我的经度、维度怎么去设置，实际显示的位置信息总会有偏移。\n发现问题 我修改照片GPS过程如下：\n通过高德地图 或 百度地图 找到需要的坐标经纬度，比如设置故宫博物院位置信息。\n通过命令修改照片信息\nexiftool -P -GPSLongitudeRef=E -GPSLongitude=116.403694 -GPSLatitudeRef=N -GPSLatitude=39.923568 -GPSAltitudeRef=Above -GPSAltitude=0 mm.jpg 验证修改后的地理位置\n你会发现修改后的位置和自己在地图中挑选的地点有偏差。\n解决问题 经过几次搜索了解到，各大地图经纬度标注都不统一，大致有三种标准：\nWGS84：为一种大地坐标系，也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。\nGCJ02：又称火星坐标系，是由中国国家测绘局制定的地理坐标系统，是由WGS84加密后得到的坐标系。\nBD09：为百度坐标系，在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标，bd09mc表示百度墨卡托米制坐标。\n照片中的GPS信息是WGS84标准，通过百度地图、高德地图获取到的坐标就不准确，最终导致照片位置偏移。\n通过坐标转换工具转换得到新的坐标 [116.39744836136032, 39.92216334299967]，将这个坐标配置在照片上就能正确标注位置\n参考 【GIS】高德地图、百度地图的经纬度科普！MySQL和编程语言是如何计算两个坐标之间的距离的？\n提供了百度坐标（BD09）、国测局坐标（火星坐标，GCJ02）、和WGS84坐标系之间的转换\n坐标转换模块\n","date":"2023-08-03T03:52:28-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=5","permalink":"https://zfj1441.com/p/%E5%85%B3%E4%BA%8Eexiftool%E4%BF%AE%E6%94%B9%E7%85%A7%E7%89%87%E6%8B%8D%E6%91%84%E5%9C%B0%E7%82%B9/","title":"关于exiftool修改照片拍摄地点"},{"content":"前言 本人习惯使用NAS管理照片，我会把我所有设备所有照片图片都上传到NAS，后期通过时间线回顾及查询。当然这都依赖管理软件读取照片的拍摄地点、拍摄时间、拍摄设备等做排序归类。但我经常遇到有些照片没有以上信息，导致照片归类非常的混乱。后面经过几番搜索找到个不错的照片信息修改工具。windwos上有MagicEXIF、ExifToolGUI,linux下有exiftool。\nExifTool介绍 ExifTool由Phil Harvey开发，是一款免费、跨平台的开源软件，用于读写和处理图像、音频、视频和PDF等文件的元数据。简单来说Exiftool可以读写照片中exif数据达到修改拍摄日期和时间、GPS定位（拍摄地点）甚至是拍摄的设备型号等。 以下是我使用exiftool修改拍摄照片的例子。\n# 删除照片所有Photoshop信息 exiftool -Photoshop:All= dst.jpg # 删除照片GPS信息 exiftool -gps:all= photo.jpg # 修改创建时间 exiftool -CreateDate=\u0026#39;2021-01-01 11:22:33\u0026#39; mm.jpg # 修改照片拍摄时间 exiftool -DateTimeOriginal=\u0026#39;2021-01-01 11:22:33\u0026#39; mm.jpg # 修改照片拍摄地点（GPS信息） exiftool -P -GPSLongitudeRef=E -GPSLongitude=111.123456 -GPSLatitudeRef=N -GPSLatitude=33.23456 -GPSAltitudeRef=Above -GPSAltitude=357 mm.jpg # 删除照片所有信息(防止信息泄漏) exiftool -all= demo.jpg # 批量删除照片所有信息(防止信息泄漏) find . -name \u0026#34;*.jp*g\u0026#34; | xargs exiftool -all= 参考 ExifTool官网\nExifTool完全入门指南\n","date":"2023-07-15T03:51:02-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=4","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8exiftool%E4%BF%AE%E6%94%B9%E7%85%A7%E7%89%87%E6%8B%8D%E6%91%84%E6%97%A5%E6%9C%9F%E6%97%B6%E9%97%B4/","title":"使用exiftool修改照片拍摄日期时间"},{"content":"前言 今天临时需要在办公环境安装个数据库，简单记录下安装过程及中间问题解决方案。\nmysql安装过程 下载MySql 安装包下载MySQL Community Server\n解压放在D:\\Program Files下\n配置 创建数据目录和配置文件\nD:\\Program Files\\mysql-5.7.41-winx64\\data\\\nD:\\Program Files\\mysql-5.7.41-winx64\\my.ini\n[mysql] auto-rehash default-character-set = utf8mb4 connect-timeout = 3 [mysqld] skip-name-resolve bind-address=0.0.0.0 port = 3306 # 设置mysql的安装目录 basedir=D:\\Program Files\\mysql-5.7.41-winx64 # 设置mysql数据库的数据的存放目录 datadir=D:\\Program Files\\mysql-5.7.41-winx64\\data # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8mb4 # 创建新表时将使用的默认存储引擎 default-storage-engine = INNODB # myisam 或 INNODB #innodb_buffer_pool_size = 100M lower_case_table_names=1 max_allowed_packet=16M sql_mode=\u0026#39;STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION\u0026#39; #skip-grant-tables # 放开次参数root免密码登录 启动 设置环境变量 PATH中加入$MYSQL_HOME/bin\n在CMD命令下执行\n# 安装数据库 mysqld --install # 初始化数据库 mysqld --initialize --console # 此命令结束后会打印root默认密码 # 启动数据库 net start mysql # 停止数据库 net stop mysql # 登录数据库 mysql -u root -p # 创建数据库、建表、建用户等等.... 问题处理 问题1 提示错误信息：ERROR 1130 : Host \u0026lsquo;127.0.0.1\u0026rsquo; is not allowed to connect to this MySQL server\nstack overflow第7条回复\nI had the same message after a fresh installation with the no-install zip and solved it as follows. Perhaps this could have been a solution for your problem too:\nStop the MySQL server or service. Open a Command Prompt window with administrative rights and go to the bin folder in the MySQL install directory. Start MySQL with skip-grants-table and don\u0026rsquo;t forget your config file: mysqld \u0026ndash;defaults-file=[filename] \u0026ndash;skip-grant-tables\nOpen another Command Prompt window and go to the bin folder again.\nNow you can login:\nmysql -u root -p\nShow the users with: SELECT user, host FROM mysql.user;\nVerify there is one \u0026lsquo;root\u0026rsquo; with host \u0026rsquo;localhost\u0026rsquo;.\nChange the host:\nUPDATE mysql.user SET host=\u0026rsquo;%\u0026rsquo; WHERE user=\u0026lsquo;root\u0026rsquo;;\nExit the mysql program and close the Command Prompt window.\nType Ctrl-C in the other Command Prompt window to stop the server, then close the Command Prompt Window.\nStart MySQL as you normally would and verify that you can login.\n问题2 提示错误信息：系统找不到制定的文件。\n打开注册表 \u0026ldquo;我的计算机\\HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\services\\MySQL\u0026quot;下ImagePath配置改成\u0026quot;D:\\Program Files\\mysql-5.7.41-winx64\\bin\\mysqld\u0026rdquo; MySQL\nCSDN-MySQL报错\n问题3 忘记root用户密码\n在my.ini配置文件中加入 skip-grant-tables 重新启停mysql，在用root免密码方式登录 修改密码UPDATE user SET authentication_string=password('xxxxxx') WHERE user='root'; MySql5.7重置root密码\n最后 简单记录下简答安装过程，以免后续还需要用。\n参考 CSDN-Windows安装MySQL 5.7(Zip)\nstack overflow第7条回复\nCSDN-MySQL报错\nMySql5.7重置root密码\n","date":"2023-06-05T03:38:55-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=win7","permalink":"https://zfj1441.com/p/windows7%E7%8E%AF%E5%A2%83zip%E6%96%B9%E5%BC%8F%E5%AE%89%E8%A3%85mysql/","title":"windows7环境zip方式安装mysql"},{"content":"背景 在windows下用python开发了批量txt转xlsx工具，用的pyinstaller打包成了exe系统。国产化下越来越多的电脑已经改成了统信UOS，所以琢磨着重新开发套。新txt转xlsx工具采用go-gtk开发编译，本文简单貌似下已经编辑好的二进制制作成deb分享给朋友。\n打包deb应用 准备签名证书 UOS官方网站注册https://www.chinauos.com/,保存账号、密码 在应用商店中下载\u0026quot;cert-tool\u0026quot;应用并安装 并按下面命令生产证书 cert-tool -username=uos_zhanghao -password=uos_mima 准备deb包内容 $ tree -L 4 org.deepin.txt2xlsx org.deepin.txt2xlsx ├── DEBIAN │ ├── control │ ├── postinst │ └── postrm ├── opt │ └── apps │ └── org.deepin.txt2xlsx │ ├── entries │ ├── files │ └── info └── usr └── share ├── applications │ └── org.deepin.txt2xlsx.desktop └── icons └── hicolor 打包并签名 dpkg-deb -b org.deepin.txt2xlsx deepin-elf-sign-deb org.deepin.txt2xlsx.deb 后记 随着国产化的推进，国产终端会越来越多转想UOS也是趋势，所以多搞搞国产软件开发。\ndeb目录结构.zip\n参考内容 UOS应用打包规范\nUOS开发者调试签名\nUOS系统如何给软件签名\nLinux制作deb格式安装包教程\nUOS非开发者模式deb打包安装说明\n","date":"2023-05-28T03:36:22-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=2","permalink":"https://zfj1441.com/p/uos%E5%88%B6%E4%BD%9Cdeb%E6%A0%BC%E5%BC%8F%E5%AE%89%E8%A3%85%E5%8C%85%E6%95%99%E7%A8%8B/","title":"Uos制作deb格式安装包教程"},{"content":"背景 说说为什么想用hexo搭建博客，年初用typecho+Jeo搭的博客，从界面上功能上我都是比较满意的。但最近看上另外一个精美博客，用的是hexo+Butterfly，正好手头有台空闲服务器。\n环境准备 博客配置 hexo(框架) + Butterfly(主题) + valine(评论)\n# root用户下执行 yum install nginx yum install nodejs yum install npm yum install git # 普通用户下执行 echo \u0026#34;prefix=~/.npm\u0026#34; \u0026gt; ~/.npmrc echo \u0026#34;export PATH=$PATH:$HOME/.npm/bin\u0026#34; \u0026gt;\u0026gt; ~/.bash_profile source ~/.bash_profile 博客安装 # 普通用户下执行 # 安装hexo npm install -g hexo-cli hexo init myblog cd myblog npm install # butterfly主题 cd myblog/themes git clone -b master https://github.com/jerryc127/hexo-theme-butterfly.git butterfly 站点配置文件 # Hexo Configuration ## Docs: https://hexo.io/docs/configuration.html ## Source: https://github.com/hexojs/hexo/ # Site title: Junes博客 subtitle: \u0026#39;\u0026#39; description: \u0026#39;\u0026#39; keywords: author: Junes language: zh-CN timezone: \u0026#39;Asia/Shanghai\u0026#39; # URL ## Set your site url here. For example, if you use GitHub Page, set url as \u0026#39;https://username.github.io/project\u0026#39; url: https://zfj1441.eu.org permalink: :year/:month/:day/:title/ permalink_defaults: pretty_urls: trailing_index: true # Set to false to remove trailing \u0026#39;index.html\u0026#39; from permalinks trailing_html: true # Set to false to remove trailing \u0026#39;.html\u0026#39; from permalinks # Directory source_dir: source public_dir: public tag_dir: tags archive_dir: archives category_dir: categories code_dir: downloads/code i18n_dir: :lang skip_render: # Writing new_post_name: :title.md # File name of new posts default_layout: post titlecase: false # Transform title into titlecase external_link: enable: true # Open external links in new tab field: site # Apply to the whole site exclude: \u0026#39;\u0026#39; filename_case: 0 render_drafts: false post_asset_folder: false relative_link: false future: true highlight: enable: true line_number: true auto_detect: false tab_replace: \u0026#39;\u0026#39; wrap: true hljs: false prismjs: enable: false preprocess: true line_number: true tab_replace: \u0026#39;\u0026#39; # Home page setting # path: Root path for your blogs index page. (default = \u0026#39;\u0026#39;) # per_page: Posts displayed per page. (0 = disable pagination) # order_by: Posts order. (Order by date descending by default) index_generator: path: \u0026#39;\u0026#39; per_page: 10 order_by: -date # Category \u0026amp; Tag default_category: uncategorized category_map: tag_map: # Metadata elements ## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta meta_generator: true # Date / Time format ## Hexo uses Moment.js to parse and display date ## You can customize the date format as defined in ## http://momentjs.com/docs/#/displaying/format/ date_format: YYYY-MM-DD time_format: HH:mm:ss ## updated_option supports \u0026#39;mtime\u0026#39;, \u0026#39;date\u0026#39;, \u0026#39;empty\u0026#39; updated_option: \u0026#39;mtime\u0026#39; # Pagination ## Set per_page to 0 to disable pagination per_page: 10 pagination_dir: page # Include / Exclude file(s) ## include:/exclude: options only apply to the \u0026#39;source/\u0026#39; folder include: exclude: ignore: # Extensions ## Plugins: https://hexo.io/plugins/ ## Themes: https://hexo.io/themes/ theme: butterfly # Deployment ## Docs: https://hexo.io/docs/one-command-deployment deploy: type: \u0026#39;\u0026#39; 站点配置文件 # Navigation bar settings (導航欄設置) # see https://butterfly.js.org/posts/4aa8abbe/##導航欄設置-Navigation-bar-settings # -------------------------------------- nav: logo: # image display_title: true fixed: false # fixed navigation bar # Menu 目錄 menu: 主页: / || fas fa-home 档案: /archives/ || fas fa-archive 标签: /tags/ || fas fa-tags # Categories: /categories/ || fas fa-folder-open # List||fas fa-list: # Music: /music/ || fas fa-music # Movie: /movies/ || fas fa-video # : /link/ || fas fa-link 关于: /about/ || fas fa-heart # Code Blocks (代碼相關) # -------------------------------------- highlight_theme: light # darker / pale night / light / ocean / mac / mac light / false highlight_copy: true # copy button highlight_lang: true # show the code language highlight_shrink: false # true: shrink the code blocks / false: expand the code blocks | none: expand code blocks and hide the button highlight_height_limit: false # unit: px code_word_wrap: false # Social Settings (社交圖標設置) # formal: # icon: link || the description || color social: fas fa-envelope: mailto:zfj1441@163.com || Email || \u0026#34;#000000\u0026#34; # fab fa-github: https://github.com/xxxxx || Github || \u0026#39;#24292e\u0026#39; # fas fa-envelope: mailto:xxxxxx@gmail.com || Email || \u0026#39;#4a7dbe\u0026#39; # Image (圖片設置) # -------------------------------------- # Favicon（網站圖標） favicon: /img/favicon.png # Avatar (頭像) avatar: img: https://i.loli.net/2021/02/24/5O1day2nriDzjSu.png effect: false # Disable all banner image disable_top_img: false # The banner image of home page index_img: /images/index.jpg # If the banner of page not setting, it will show the top_img default_top_img: /images/index.jpg # The banner image of archive page archive_img: # If the banner of tag page not setting, it will show the top_img # note: tag page, not tags page (子標籤頁面的 top_img) tag_img: # The banner image of tag page # format: # - tag name: xxxxx tag_per_img: # If the banner of category page not setting, it will show the top_img # note: category page, not categories page (子分類頁面的 top_img) category_img: # The banner image of category page # format: # - category name: xxxxx category_per_img: cover: # display the cover or not (是否顯示文章封面) index_enable: true aside_enable: true archives_enable: true # the position of cover in home page (封面顯示的位置) # left/right/both position: both # When cover is not set, the default cover is displayed (當沒有設置cover時，默認的封面顯示) default_cover: - https://i.loli.net/2020/05/01/gkihqEjXxJ5UZ1C.jpg # Replace Broken Images (替換無法顯示的圖片) error_img: flink: /img/friend_404.gif post_page: /img/404.jpg # A simple 404 page error_404: enable: true subtitle: \u0026#39;Page Not Found\u0026#39; background: https://i.loli.net/2020/05/19/aKOcLiyPl2JQdFD.png post_meta: page: # Home Page date_type: created # created or updated or both 主頁文章日期是創建日或者更新日或都顯示 date_format: date # date/relative 顯示日期還是相對日期 categories: true # true or false 主頁是否顯示分類 tags: false # true or false 主頁是否顯示標籤 label: true # true or false 顯示描述性文字 post: date_type: both # created or updated or both 文章頁日期是創建日或者更新日或都顯示 date_format: date # date/relative 顯示日期還是相對日期 categories: true # true or false 文章頁是否顯示分類 tags: true # true or false 文章頁是否顯示標籤 label: true # true or false 顯示描述性文字 # Display the article introduction on homepage # 1: description # 2: both (if the description exists, it will show description, or show the auto_excerpt) # 3: auto_excerpt (default) # false: do not show the article introduction index_post_content: method: 3 length: 500 # if you set method to 2 or 3, the length need to config # anchor anchor: button: enable: false always_show: false icon: # the unicode value of Font Awesome icon, such as \u0026#39;\\3423\u0026#39; auto_update: false # when you scroll in post, the URL will update according to header id. # figcaption (圖片描述文字) photofigcaption: false # copy settings # copyright: Add the copyright information after copied content (複製的內容後面加上版權信息) copy: enable: true copyright: enable: false limit_count: 50 # Post # -------------------------------------- # toc (目錄) toc: post: true page: false number: true expand: false style_simple: false # for post scroll_percent: true post_copyright: enable: true decode: false author_href: license: CC BY-NC-SA 4.0 license_url: https://creativecommons.org/licenses/by-nc-sa/4.0/ # Sponsor/reward reward: enable: false QR_code: # - img: /img/wechat.jpg # link: # text: wechat # - img: /img/alipay.jpg # link: # text: alipay # Post edit # Easily browse and edit blog source code online. post_edit: enable: false # url: https://github.com/user-name/repo-name/edit/branch-name/subdirectory-name/ # For example: https://github.com/jerryc127/butterfly.js.org/edit/main/source/ url: # Related Articles related_post: enable: true limit: 6 # Number of posts displayed date_type: created # or created or updated 文章日期顯示創建日或者更新日 # post_pagination (分頁) # value: 1 || 2 || false # 1: The \u0026#39;next post\u0026#39; will link to old post # 2: The \u0026#39;next post\u0026#39; will link to new post # false: disable pagination post_pagination: 1 # Displays outdated notice for a post (文章過期提醒) noticeOutdate: enable: false style: flat # style: simple/flat limit_day: 500 # When will it be shown position: top # position: top/bottom message_prev: It has been message_next: days since the last update, the content of the article may be outdated. # Footer Settings # -------------------------------------- footer: owner: enable: true since: 2020 custom_text: copyright: true # Copyright of theme and framework # aside (側邊欄) # -------------------------------------- aside: enable: true hide: false button: true mobile: false # display on mobile position: right # left or right display: archive: true tag: true category: true card_author: enable: true description: button: enable: false icon: fab fa-github text: Follow Me link: https://github.com/xxxxxx card_announcement: enable: true content: This is my Blog card_recent_post: enable: true limit: 5 # if set 0 will show all sort: date # date or updated sort_order: # Don\u0026#39;t modify the setting unless you know how it works card_categories: enable: true limit: 8 # if set 0 will show all expand: none # none/true/false sort_order: # Don\u0026#39;t modify the setting unless you know how it works card_tags: enable: true limit: 40 # if set 0 will show all color: false orderby: random # Order of tags, random/name/length order: 1 # Sort of order. 1, asc for ascending; -1, desc for descending sort_order: # Don\u0026#39;t modify the setting unless you know how it works card_archives: enable: true type: monthly # yearly or monthly format: YYYY年MM月 # eg: YYYY年MM月 order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending limit: 8 # if set 0 will show all sort_order: # Don\u0026#39;t modify the setting unless you know how it works card_webinfo: enable: true post_count: true last_push_date: true sort_order: # Don\u0026#39;t modify the setting unless you know how it works # busuanzi count for PV / UV in site # 訪問人數 busuanzi: site_uv: true site_pv: true page_pv: true # Time difference between publish date and now (網頁運行時間) # Formal: Month/Day/Year Time or Year/Month/Day Time runtimeshow: enable: true publish_date: 5/20/2023 00:00:00 # Aside widget - Newest Comments newest_comments: enable: false sort_order: # Don\u0026#39;t modify the setting unless you know how it works limit: 6 storage: 10 # unit: mins, save data to localStorage avatar: true # Bottom right button (右下角按鈕) # -------------------------------------- # Conversion between Traditional and Simplified Chinese (簡繁轉換) translate: enable: true # The text of a button default: 繁 # the language of website (1 - Traditional Chinese/ 2 - Simplified Chinese） defaultEncoding: 2 # Time delay translateDelay: 0 # The text of the button when the language is Simplified Chinese msgToTraditionalChinese: \u0026#39;繁\u0026#39; # The text of the button when the language is Traditional Chinese msgToSimplifiedChinese: \u0026#39;簡\u0026#39; # Read Mode (閲讀模式) readmode: true # dark mode darkmode: enable: true # Toggle Button to switch dark/light mode button: true # Switch dark/light mode automatically (自動切換 dark mode和 light mode) # autoChangeMode: 1 Following System Settings, if the system doesn\u0026#39;t support dark mode, it will switch dark mode between 6 pm to 6 am # autoChangeMode: 2 Switch dark mode between 6 pm to 6 am # autoChangeMode: false autoChangeMode: false # Set the light mode time. The value is between 0 and 24. If not set, the default value is 6 and 18 start: # 8 end: # 22 # show scroll percent in scroll-to-top button rightside_scroll_percent: false # Don\u0026#39;t modify the following settings unless you know how they work (非必要請不要修改 ) # Choose: readmode,translate,darkmode,hideAside,toc,chat,comment # Don\u0026#39;t repeat 不要重複 rightside_item_order: enable: false hide: # readmode,translate,darkmode,hideAside show: # toc,chat,comment # Math (數學) # -------------------------------------- # About the per_page # if you set it to true, it will load mathjax/katex script in each page (true 表示每一頁都加載js) # if you set it to false, it will load mathjax/katex script according to your setting (add the \u0026#39;mathjax: true\u0026#39; in page\u0026#39;s front-matter) # (false 需要時加載，須在使用的 Markdown Front-matter 加上 mathjax: true) # MathJax mathjax: enable: false per_page: false # KaTeX katex: enable: false per_page: false hide_scrollbar: true # search (搜索) # see https://butterfly.js.org/posts/ceeb73f/#搜索系統 # -------------------------------------- # Algolia search algolia_search: enable: false hits: per_page: 6 # Local search local_search: enable: false # Preload the search data when the page loads. preload: false # Show top n results per article, show all results by setting to -1 top_n_per_article: 1 # Unescape html strings to the readable one. unescape: false CDN: # Docsearch docsearch: enable: false appId: apiKey: indexName: option: # Share System (分享) # -------------------------------------- # AddThis # https://www.addthis.com/ addThis: enable: false pubid: # Share.js # https://github.com/overtrue/share.js sharejs: enable: true sites: facebook,twitter,wechat,weibo,qq # AddToAny # https://www.addtoany.com/ addtoany: enable: false item: facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link # Comments System # -------------------------------------- comments: # Up to two comments system, the first will be shown as default # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/Artalk use: Valine # Valine,Disqus text: false # Display the comment name next to the button # lazyload: The comment system will be load when comment element enters the browser\u0026#39;s viewport. # If you set it to true, the comment count will be invalid lazyload: false count: false # Display comment count in post\u0026#39;s top_img card_post_count: false # Display comment count in Home Page # disqus # https://disqus.com/ disqus: shortname: apikey: # For newest comments widget # Alternative Disqus - Render comments with Disqus API # DisqusJS 評論系統，可以實現在網路審查地區載入 Disqus 評論列表，兼容原版 # https://github.com/SukkaW/DisqusJS disqusjs: shortname: apikey: option: # livere (來必力) # https://www.livere.com/ livere: uid: # gitalk # https://github.com/gitalk/gitalk gitalk: client_id: client_secret: repo: owner: admin: option: # valine # https://valine.js.org valine: appId: xxx appKey: xxxx avatar: retro serverURLs: //xxx.zfj1441.eu.org bg: # valine background visitor: true option: requiredFields: [\u0026#39;nick\u0026#39;,\u0026#39;mail\u0026#39;] placeholder: 说说你的想法~ lang: zh-CN # waline - A simple comment system with backend support fork from Valine # https://waline.js.org/ waline: serverURL: # Waline server address url bg: # waline background pageview: false option: # utterances # https://utteranc.es/ utterances: repo: # Issue Mapping: pathname/url/title/og:title issue_term: pathname # Theme: github-light/github-dark/github-dark-orange/icy-dark/dark-blue/photon-dark light_theme: github-light dark_theme: photon-dark # Facebook Comments Plugin # https://developers.facebook.com/docs/plugins/comments/ facebook_comments: app_id: user_id: # optional pageSize: 10 # The number of comments to show order_by: social # social/time/reverse_time lang: zh_TW # Language en_US/zh_CN/zh_TW and so on # Twikoo # https://github.com/imaegoo/twikoo twikoo: envId: region: visitor: false option: # Giscus # https://giscus.app/ giscus: repo: repo_id: category_id: theme: light: light dark: dark option: # Remark42 # https://remark42.com/docs/configuration/frontend/ remark42: host: # Your Host URL siteId: # Your Site ID option: # Artalk # https://artalk.js.org/guide/frontend/config.html artalk: server: site: Junes visitor: true option: # Chat Services # -------------------------------------- # Chat Button [recommend] # It will create a button in the bottom right corner of website, and hide the origin button chat_btn: false # The origin chat button is displayed when scrolling up, and the button is hidden when scrolling down chat_hide_show: false # chatra # https://chatra.io/ chatra: enable: false id: # tidio # https://www.tidio.com/ tidio: enable: false public_key: # daovoice # http://dashboard.daovoice.io/app daovoice: enable: false app_id: # crisp # https://crisp.chat/en/ crisp: enable: false website_id: # messenger # https://developers.facebook.com/docs/messenger-platform/discovery/facebook-chat-plugin/ messenger: enable: false pageID: lang: zh_TW # Language en_US/zh_CN/zh_TW and so on # Analysis # -------------------------------------- # Baidu Analytics # https://tongji.baidu.com/web/welcome/login baidu_analytics: # Google Analytics # https://analytics.google.com/analytics/web/ google_analytics: # Cloudflare Analytics # https://www.cloudflare.com/zh-tw/web-analytics/ cloudflare_analytics: # Microsoft Clarity # https://clarity.microsoft.com/ microsoft_clarity: # Advertisement # -------------------------------------- # Google Adsense (谷歌廣告) google_adsense: enable: false auto_ads: true js: https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js client: enable_page_level_ads: true # Insert ads manually (手動插入廣告) # ad: # index: # aside: # post: # Verification (站長驗證) # -------------------------------------- site_verification: # - name: google-site-verification # content: xxxxxx # - name: baidu-site-verification # content: xxxxxxx # Beautify/Effect (美化/效果) # -------------------------------------- # Theme color for customize # Notice: color value must in double quotes like \u0026#34;#000\u0026#34; or may cause error! theme_color: enable: true main: \u0026#34;#49B1F5\u0026#34; paginator: \u0026#34;#00c4b6\u0026#34; button_hover: \u0026#34;#FF7242\u0026#34; text_selection: \u0026#34;#00c4b6\u0026#34; link_color: \u0026#34;#99a9bf\u0026#34; meta_color: \u0026#34;#858585\u0026#34; hr_color: \u0026#34;#A4D8FA\u0026#34; code_foreground: \u0026#34;#F47466\u0026#34; code_background: \u0026#34;rgba(27, 31, 35, .05)\u0026#34; toc_color: \u0026#34;#00c4b6\u0026#34; blockquote_padding_color: \u0026#34;#49b1f5\u0026#34; blockquote_background_color: \u0026#34;#49b1f5\u0026#34; scrollbar_color: \u0026#34;#49b1f5\u0026#34; meta_theme_color_light: \u0026#34;ffffff\u0026#34; meta_theme_color_dark: \u0026#34;#0d0d0d\u0026#34; # The top_img settings of home page # default: top img - full screen, site info - middle (默認top_img全屏，site_info在中間) # The position of site info, eg: 300px/300em/300rem/10% (主頁標題距離頂部距離) index_site_info_top: # The height of top_img, eg: 300px/300em/300rem (主頁top_img高度) index_top_img_height: 20rem # The user interface setting of category and tag page (category和tag頁的UI設置) # index - same as Homepage UI (index 值代表 UI將與首頁的UI一樣) # default - same as archives UI 默認跟archives頁面UI一樣 category_ui: # 留空或 index tag_ui: # 留空或 index # Website Background (設置網站背景) # can set it to color or image (可設置圖片 或者 顔色) # The formal of image: url(http://xxxxxx.com/xxx.jpg) background: # Footer Background footer_bg: false # the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px) rightside-bottom: # Enter transitions (開啓網頁進入效果) enter_transitions: true # Typewriter Effect (打字效果) # https://github.com/disjukr/activate-power-mode activate_power_mode: enable: false colorful: true # open particle animation (冒光特效) shake: true # open shake (抖動特效) mobile: false # Background effects (背景特效) # -------------------------------------- # canvas_ribbon (靜止彩帶背景) # See: https://github.com/hustcc/ribbon.js canvas_ribbon: enable: true size: 150 alpha: 0.6 zIndex: -1 click_to_change: false mobile: true # Fluttering Ribbon (動態彩帶) canvas_fluttering_ribbon: enable: false mobile: false # canvas_nest # https://github.com/hustcc/canvas-nest.js canvas_nest: enable: false color: \u0026#39;0,0,255\u0026#39; #color of lines, default: \u0026#39;0,0,0\u0026#39;; RGB values: (R,G,B).(note: use \u0026#39;,\u0026#39; to separate.) opacity: 0.7 # the opacity of line (0~1), default: 0.5. zIndex: -1 # z-index property of the background, default: -1. count: 99 # the number of lines, default: 99. mobile: false # Mouse click effects: fireworks (鼠標點擊效果: 煙火特效) fireworks: enable: false zIndex: 9999 # -1 or 9999 mobile: false # Mouse click effects: Heart symbol (鼠標點擊效果: 愛心) click_heart: enable: false mobile: false # Mouse click effects: words (鼠標點擊效果: 文字) ClickShowText: enable: false text: # - I # - LOVE # - YOU fontSize: 15px random: false mobile: false # Default display mode (網站默認的顯示模式) # light (default) / dark display_mode: light # Beautify (美化頁面顯示) beautify: enable: false field: post # site/post title-prefix-icon: # \u0026#39;\\f0c1\u0026#39; title-prefix-icon-color: # \u0026#39;#F47466\u0026#39; # Global font settings # Don\u0026#39;t modify the following settings unless you know how they work (非必要不要修改) font: global-font-size: code-font-size: font-family: code-font-family: # Font settings for the site title and site subtitle # 左上角網站名字 主頁居中網站名字 blog_title_font: font_link: font-family: # The setting of divider icon (水平分隔線圖標設置) hr_icon: enable: true icon: # the unicode value of Font Awesome icon, such as \u0026#39;\\3423\u0026#39; icon-top: # the subtitle on homepage (主頁subtitle) subtitle: enable: false # Typewriter Effect (打字效果) effect: true # Customize typed.js (配置typed.js) # https://github.com/mattboldt/typed.js/#customization typed_option: # source 調用第三方服務 # source: false 關閉調用 # source: 1 調用一言網的一句話（簡體） https://hitokoto.cn/ # source: 2 調用一句網（簡體） https://yijuzhan.com/ # source: 3 調用今日詩詞（簡體） https://www.jinrishici.com/ # subtitle 會先顯示 source , 再顯示 sub 的內容 source: false # 如果關閉打字效果，subtitle 只會顯示 sub 的第一行文字 sub: # Loading Animation (加載動畫) preloader: enable: false # source # 1. fullpage-loading # 2. pace (progress bar) source: 1 # pace theme (see https://codebyzach.github.io/pace/) pace_css_url: # wordcount (字數統計) # see https://butterfly.js.org/posts/ceeb73f/#字數統計 wordcount: enable: false post_wordcount: true min2read: true total_wordcount: true # Lightbox (圖片大圖查看模式) # -------------------------------------- # You can only choose one, or neither (只能選擇一個 或者 兩個都不選) # medium-zoom # https://github.com/francoischalifour/medium-zoom medium_zoom: false # fancybox # https://fancyapps.com/fancybox/ fancybox: true # Tag Plugins settings (標籤外掛) # -------------------------------------- # abcjs (樂譜渲染) # See https://github.com/paulrosen/abcjs abcjs: enable: false per_page: true # mermaid # see https://github.com/mermaid-js/mermaid mermaid: enable: false # built-in themes: default/forest/dark/neutral theme: light: default dark: dark # Note (Bootstrap Callout) note: # Note tag style values: # - simple bs-callout old alert style. Default. # - modern bs-callout new (v2-v3) alert style. # - flat flat callout style with background, like on Mozilla or StackOverflow. # - disabled disable all CSS styles import of note tag. style: flat icons: true border_radius: 3 # Offset lighter of background in % for modern and flat styles (modern: -12 | 12; flat: -18 | 6). # Offset also applied to label tag variables. This option can work with disabled note tag. light_bg_offset: 0 # other # -------------------------------------- # Pjax # It may contain bugs and unstable, give feedback when you find the bugs. # https://github.com/MoOx/pjax pjax: enable: false exclude: # - xxxx # - xxxx # Inject the css and script (aplayer/meting) aplayerInject: enable: false per_page: true # Snackbar (Toast Notification 彈窗) # https://github.com/polonel/SnackBar # position 彈窗位置 # 可選 top-left / top-center / top-right / bottom-left / bottom-center / bottom-right snackbar: enable: false position: bottom-left bg_light: \u0026#39;#49b1f5\u0026#39; # The background color of Toast Notification in light mode bg_dark: \u0026#39;#1f1f1f\u0026#39; # The background color of Toast Notification in dark mode # https://instant.page/ # prefetch (預加載) instantpage: false # https://github.com/vinta/pangu.js # Insert a space between Chinese character and English character (中英文之間添加空格) pangu: enable: false field: site # site/post # Lazyload (圖片懶加載) # https://github.com/verlok/vanilla-lazyload lazyload: enable: false field: site # site/post placeholder: blur: false # PWA # See https://github.com/JLHwung/hexo-offline # --------------- # pwa: # enable: false # manifest: /pwa/manifest.json # apple_touch_icon: /pwa/apple-touch-icon.png # favicon_32_32: /pwa/32.png # favicon_16_16: /pwa/16.png # mask_icon: /pwa/safari-pinned-tab.svg # Open graph meta tags # https://developers.facebook.com/docs/sharing/webmasters/ Open_Graph_meta: enable: true option: # twitter_card: # twitter_image: # twitter_id: # twitter_site: # google_plus: # fb_admins: # fb_app_id: # Add the vendor prefixes to ensure compatibility css_prefix: true # Inject # Insert the code to head (before \u0026#39;\u0026lt;/head\u0026gt;\u0026#39; tag) and the bottom (before \u0026#39;\u0026lt;/body\u0026gt;\u0026#39; tag) # 插入代码到头部 \u0026lt;/head\u0026gt; 之前 和 底部 \u0026lt;/body\u0026gt; 之前 inject: head: # - \u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;/xxx.css\u0026#34;\u0026gt; bottom: # - \u0026lt;script src=\u0026#34;xxxx\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; # CDN # Don\u0026#39;t modify the following settings unless you know how they work # 非必要請不要修改 CDN: # The CDN provider of internal scripts (主題內部 js 的 cdn 配置) # option: local/jsdelivr/unpkg/cdnjs/custom # Dev version can only choose. ( dev版的主題只能設置為 local ) internal_provider: local # The CDN provider of third party scripts (第三方 js 的 cdn 配置) # option: local/jsdelivr/unpkg/cdnjs/custom # when set it to local, you need to install hexo-butterfly-extjs third_party_provider: jsdelivr # Add version number to url, true or false version: false # Custom format # For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file} custom_format: option: # main_css: # main: # utils: # translate: # local_search: # algolia_js: # algolia_search: # instantsearch: # docsearch_js: # docsearch_css: # pjax: # gitalk: # gitalk_css: # blueimp_md5: # valine: # disqusjs: # disqusjs_css: # twikoo: # waline_js: # waline_css: # giscus: # sharejs: # sharejs_css: # mathjax: # katex: # katex_copytex: # mermaid: # canvas_ribbon: # canvas_fluttering_ribbon: # canvas_nest: # lazyload: # instantpage: # typed: # pangu: # fancybox_css: # fancybox: # medium_zoom: # snackbar_css: # snackbar: # activate_power_mode: # fireworks: # click_heart: # ClickShowText: # fontawesome: # flickr_justified_gallery_js: # flickr_justified_gallery_css: # aplayer_css: # aplayer_js: # meting_js: # prismjs_js: # prismjs_lineNumber_js: # prismjs_autoloader: # artalk_js: # artalk_css: # busuanzi: # abcjs_basic_js: 插件 cd myblog npm install hexo-renderer-pug --save npm install hexo-renderer-stylus --save npm install hexo-generator-feed --save #R SS npm install hexo-deployer-git --save # git部署 npm install hexo-generator-searchdb --save # 搜索 npm install hexo-generator-seo-friendly-sitemap --save # sitemap npm install hexo-generator-baidu-sitemap --save # 百度sitemap 后记 hexo配置下来还是需要点时间精力的，相比typecho确实麻烦很多。\n参考 hexo框架 Butterfly配置 valine介绍\n","date":"2023-05-27T03:31:37-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=1","permalink":"https://zfj1441.com/p/hexo%E5%8D%9A%E5%AE%A2%E9%85%8D%E7%BD%AE%E7%AC%94%E8%AE%B0/","title":"Hexo博客配置笔记"},{"content":"环境搭建 golang环境 go get github.com/mattn/go-gtk/gtk\ngtk依赖库 sudo apt-get install libgtk2.0-dev libglib2.0-dev libgtksourceview2.0-dev\n开启CGO_ENABLED编译环境 go env -w CGO_ENABLED=1\ngo-gtk示例\npackage main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;github.com/mattn/go-gtk/gtk\u0026#34; ) func main() { gtk.Init(\u0026amp;os.Args) win := gtk.NewWindow(gtk.WINDOW_TOPLEVEL) win.SetTitle(\u0026#34;go gtk\u0026#34;) win.SetSizeRequest(480, 320) win.Show() gtk.Main() } 启动程序 go run main.go\n参考资料： github/go-gtk\nGo语言图形界面开发：Go版GTK\nbbtool\n","date":"2023-05-22T03:45:31-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=3","permalink":"https://zfj1441.com/p/uos%E7%8E%AF%E5%A2%83%E4%B8%8Bgo-gtk%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2%E5%BC%80%E5%8F%91/","title":"Uos环境下go-Gtk图形界面开发"},{"content":"背景 国产化背景下，公司办公环境已经用上国产化设备，系统也随之切换到了统信UOSv20。已经上手个把月了说说感受，界面非常美观，软件支持也很丰富，办公是没有任何问题。但也有问题国产硬件和软件兼容性不足，体现在部分软件容易卡死。顺着对新系统的热度把笔记本也换成了系统UOS，在此记录下开发软件安装。\n软件集合 开发类 名称 安装方式 dbeaver 应用商店 jetbrains全家桶 应用商店/官网 VS Code 应用商店 RDP远程桌面 系统自带 终端 系统自带 git sudo apt install git java sudo apt install openjdk-8-jdk 办公类 名称 安装方式 WPS四件套 应用商店 微信 应用商店 QQ 应用商店 Chrome浏览器 应用商店 Obsidian 应用商店 drawio画图 github 其他 名称 安装方式 ssr加速 教程github Jetbrain破解补丁 https://3.jetbra.in/ 最后 总的来说应用商店中的软件基本上已经可以满足80%的需求，其他软件就可以自己搜索能力。\n","date":"2023-05-02T03:49:42-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=6","permalink":"https://zfj1441.com/p/%E7%BB%9F%E4%BF%A1uos%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7%E9%9B%86/","title":"统信UOS开发工具集"},{"content":"最近琢磨着在玩客云上搭博客系统，也算是废物利用。搜索一番看中typecho简单轻便，也发现已有支持该平台的镜像（yangxuan8282/typecho）但是typecho版本点老，所以在大佬的基础上重建构建了一个系统。\n修改Dockerfile文件 新版本版本typecho1.2版本对php要求是7.2，所以在大佬的基础上使用官方php的基础底包\nFROM php:7.4-apache-bullseye ENV TYPECHO_VERSION=1.2.0 COPY typecho.zip /tmp COPY entrypoint.sh /usr/local/bin/ RUN set -x \\ \u0026amp;\u0026amp; mkdir -p /usr/src/typecho \\ \u0026amp;\u0026amp; apt-get update \u0026amp;\u0026amp; apt-get install -y --no-install-recommends ca-certificates unzip \u0026amp;\u0026amp; rm -rf /var/lib/apt/lists/* \\ \u0026amp;\u0026amp; unzip -d /usr/src/typecho/ /tmp/typecho.zip \\ \u0026amp;\u0026amp; rm -rf /var/cache/apk/* \\ \u0026amp;\u0026amp; rm -rf /tmp/* WORKDIR /var/www/html EXPOSE 80 ENTRYPOINT [\u0026#34;entrypoint.sh\u0026#34;] CMD [\u0026#34;apache2-foreground\u0026#34;] 在docker hub页面上创建Docker仓库 Docker仓库\n拉取新建的仓库 docker login username docker pull username/onecloud-typecho 构建镜像并提交 docker build -t username/onecloud-typecho:dev . docker push username/onecloud-typecho:dev ","date":"2023-03-31T03:34:14-04:00","image":"https://picsum.photos/800/600?mom=302\u0026r=7","permalink":"https://zfj1441.com/p/%E8%AE%B0%E5%BD%95%E4%B8%8Bdocker%E6%9E%84%E5%BB%BA%E9%95%9C%E5%83%8F/","title":"记录下docker构建镜像"},{"content":"回顾2022年做的内容\u0026hellip;\u0026hellip;..沉默半天，感觉我是穿过到2023年的。2022立的flag基本已废弃，那么立个2023的flag。不管之后能否实现多少，让自己在迷茫的道路上有所为。\n2023年的flag flag1: 运动方面 2022年的运动基本废了，体重已达到历史以来的巅峰。那么2023年，计划2个月运动一次，步数3万步以上，截图上传NAS打卡。\nflag2: 阅读方面 2022年把《斗罗大陆》看完，《斗破苍穹》完成77%。那么2023年完成《斗破苍穹》， 最近《三体》动画开播，那就把三体三册完结了去。完成截图上传NAS打卡。\nflag3: 博客公众号 2022年把CSND内容迁移到了博客园，无新内容产出。2023年计划每季度来一篇，内容不限。完成截图上传NAS打卡。\nflag4: 撸码方面 2022年用C语言完成moniter监控程序（多年的老账总算是还上了）。2023年详细学习golang，并形成学习笔记；用golang完成4个程序\nflag5: 持续折腾 homeassistant持续折腾，体重称和空气净化器接入系统\n","date":"2023-03-20T10:43:45-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T10:43:45-04:00","permalink":"https://zfj1441.com/p/2023%E5%B9%B4%E6%88%91%E7%AB%8B%E7%9A%84flag/","title":"2023年，我立的flag"},{"content":"前言 git协同开发、版本管理神器。以往项目都是自己单干本地git做下版本管理就完事了，最近整个项目需要和同事协同完成，所以就在自己服务器上装个git服务。Git安装教程网上很多也很详细，这里简单记录下我的安装过程 。\n1、安装Git yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel yum install git #创建用户 groupadd git useradd git -g git #禁止git用户ssh终端登录。即把默认/bin/bash 改成git-shell vi /etc/passwd git:x:1002:1003::/home/git:/bin/git-shell 2、创建git密码 passwd git 3、初始化Git仓库 cd /home/git mkdir gitrepo chown git:git gitrepo/ cd gitrepo git init --bare runoob.git chown -R git:git runoob.git 4、克隆仓库 git clone ssh://git@192.168.1.100:22/home/git/gitrepo/runoob.git 输入git密码 5、新增项目 后期需要新增项目只需重复步骤3即可，为方便以后新增项目我建个脚本\n#!/bin/bash projectname=$1.git if [ ! -n \u0026#34;$1\u0026#34; ] ;then echo \u0026#34;请输入项目名称\u0026#34; echo \u0026#34;Usage: $0 myproject\u0026#34; exit fi echo $projectname git init --bare $projectname chown -R git:git $projectname echo \u0026#34;请使用如下方式连接:\u0026#34; echo \u0026#34;git clone ssh://git@服务器IP:服务器端口/home/git/gitrepo/$projectname\u0026#34; 完成以上步骤后就可以把clone链接发给朋友一起愉快的撸代码来。\n参考 https://www.runoob.com/git/git-server.html\n","date":"2022-08-19T23:50:48+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T23:50:48+08:00","permalink":"https://zfj1441.com/p/%E8%B6%85%E7%BA%A7%E7%AE%80%E5%8D%95%E7%9A%84git%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA/","title":"超级简单的Git服务器搭建"},{"content":"一、校验数字的表达式 数字：^[0-9]*$ n位的数字：^\\d{n}$ 至少n位的数字：^\\d{n,}$ m-n位的数字：^\\d{m,n}$ 零和非零开头的数字：^(0|[1-9][0-9]*)$ 非零开头的最多带两位小数的数字：^([1-9][0-9]*)+(\\.[0-9]{1,2})?$ 带1-2位小数的正数或负数：^(\\-)?\\d+(\\.\\d{1,2})$ 正数、负数、和小数：^(\\-|\\+)?\\d+(\\.\\d+)?$ 有两位小数的正实数：^[0-9]+(\\.[0-9]{2})?$ 有1~3位小数的正实数：^[0-9]+(\\.[0-9]{1,3})?$ 非零的正整数：^[1-9]\\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\\+?[1-9][0-9]*$ 非零的负整数：^\\-[1-9][]0-9\u0026#34;*$ 或 ^-[1-9]\\d*$ 非负整数：^\\d+$ 或 ^[1-9]\\d*|0$ 非正整数：^-[1-9]\\d*|0$ 或 ^((-\\d+)|(0+))$ 非负浮点数：^\\d+(\\.\\d+)?$ 或 ^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0$ 非正浮点数：^((-\\d+(\\.\\d+)?)|(0+(\\.0+)?))$ 或 ^(-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*))|0?\\.0+|0$ 正浮点数：^[1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*$ 或 ^(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*))$ 负浮点数：^-([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*)$ 或 ^(-(([0-9]+\\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\\.[0-9]+)|([0-9]*[1-9][0-9]*)))$ 浮点数：^(-?\\d+)(\\.\\d+)?$ 或 ^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$ 二、校验字符的表达式 汉字：^[\\u4e00-\\u9fa5]{0,}$ 英文和数字：^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$ 长度为3-20的所有字符：^.{3,20}$ 由26个英文字母组成的字符串：^[A-Za-z]+$ 由26个大写英文字母组成的字符串：^[A-Z]+$ 由26个小写英文字母组成的字符串：^[a-z]+$ 由数字和26个英文字母组成的字符串：^[A-Za-z0-9]+$ 由数字、26个英文字母或者下划线组成的字符串：^\\w+$ 或 ^\\w{3,20}$ 中文、英文、数字包括下划线：^[\\u4E00-\\u9FA5A-Za-z0-9_]+$ 中文、英文、数字但不包括下划线等符号：^[\\u4E00-\\u9FA5A-Za-z0-9]+$ 或 ^[\\u4E00-\\u9FA5A-Za-z0-9]{2,20}$ 可以输入含有^%\u0026amp;\u0026#39;,;=?$\\\u0026#34;等字符：[^%\u0026amp;\u0026#39;,;=?$\\x22]+ 禁止输入含有~的字符：[^~\\x22]+ 三、特殊需求表达式 Email地址：^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$ 域名：[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.? InternetURL：[a-zA-z]+://[^\\s]* 或 ^http://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%\u0026amp;=]*)?$ 手机号码：^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$ 电话号码(\u0026#34;XXX-XXXXXXX\u0026#34;、\u0026#34;XXXX-XXXXXXXX\u0026#34;、\u0026#34;XXX-XXXXXXX\u0026#34;、\u0026#34;XXX-XXXXXXXX\u0026#34;、\u0026#34;XXXXXXX\u0026#34;和\u0026#34;XXXXXXXX)：^(\\(\\d{3,4}-)|\\d{3.4}-)?\\d{7,8}$ 国内电话号码(0511-4405222、021-87888822)：\\d{3}-\\d{8}|\\d{4}-\\d{7} 电话号码正则表达式（支持手机号码，3-4位区号，7-8位直播号码，1－4位分机号）: ((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$) 身份证号(15位、18位数字)，最后一位是校验位，可能为数字或字符X：(^\\d{15}$)|(^\\d{18}$)|(^\\d{17}(\\d|X|x)$) 帐号是否合法(字母开头，允许5-16字节，允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$ 密码(以字母开头，长度在6~18之间，只能包含字母、数字和下划线)：^[a-zA-Z]\\w{5,17}$ 强密码(必须包含大小写字母和数字的组合，不能使用特殊字符，长度在8-10之间)：^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$ 日期格式：^\\d{4}-\\d{1,2}-\\d{1,2} 一年的12个月(01～09和1～12)：^(0?[1-9]|1[0-2])$ 一个月的31天(01～09和1～31)：^((0?[1-9])|((1|2)[0-9])|30|31)$ ## 四、其他 ```bash 有四种钱的表示形式我们可以接受:\u0026#34;10000.00\u0026#34; 和 \u0026#34;10,000.00\u0026#34;, 和没有 \u0026#34;分\u0026#34; 的 \u0026#34;10000\u0026#34; 和 \u0026#34;10,000\u0026#34;：^[1-9][0-9]*$ 这表示任意一个不以0开头的数字,但是,这也意味着一个字符\u0026#34;0\u0026#34;不通过,所以我们采用下面的形式：^(0|[1-9][0-9]*)$ 一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号：^(0|-?[1-9][0-9]*)$ 这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧。下面我们要加的是说明可能的小数部分：^[0-9]+(.[0-9]+)?$ 必须说明的是,小数点后面至少应该有1位数,所以\u0026#34;10.\u0026#34;是不通过的,但是 \u0026#34;10\u0026#34; 和 \u0026#34;10.2\u0026#34; 是通过的：^[0-9]+(.[0-9]{2})?$ 这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样：^[0-9]+(.[0-9]{1,2})?$ 这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样：^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$ 1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须：^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$ 备注：这就是最终结果了,别忘了\u0026#34;+\u0026#34;可以用\u0026#34;*\u0026#34;替代如果你觉得空字符串也可以接受的话(奇怪,为什么?)最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里 xml文件：^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\\\.[x|X][m|M][l|L]$ 中文字符的正则表达式：[\\u4e00-\\u9fa5] 双字节字符：[^\\x00-\\xff] (包括汉字在内，可以用来计算字符串的长度(一个双字节字符长度计2，ASCII字符计1)) 空白行的正则表达式：\\n\\s*\\r (可以用来删除空白行) HTML标记的正则表达式：\u0026lt;(\\S*?)[^\u0026gt;]*\u0026gt;.*?|\u0026lt;.*? /\u0026gt; ( 首尾空白字符的正则表达式：^\\s*|\\s*$或(^\\s*)|(\\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)，非常有用的表达式) 腾讯QQ号：[1-9][0-9]{4,} (腾讯QQ号从10000开始) 中国邮政编码：[1-9]\\d{5}(?!\\d) (中国邮政编码为6位数字) IP地址：((?:(?:25[0-5]|2[0-4]\\\\d|[01]?\\\\d?\\\\d)\\\\.){3}(?:25[0-5]|2[0-4]\\\\d|[01]?\\\\d?\\\\d)) ## 最后 互联网上收集的，不保证百分百有效请斟酌后使用 [正则表达式手册](https://tool.oschina.net/uploads/apidocs/jquery/regexp.html) ","date":"2022-05-06T23:16:38+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T23:16:38+08:00","permalink":"https://zfj1441.com/p/%E5%B8%B8%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/","title":"常用正则表达式"},{"content":"前言 刚接触go大概是在2年前，同事推荐学习但当时兴趣不高只是随手看了几篇推文了之。之后也没动想过学更没动手写，直到上周利用go打包编译能力完成一小项目，让我下定决心要学习go语言。就当前go最能力吸引我的是跨平台编译能力。\n正文 go-teleplay项目功能是跟踪新放映的电视剧，有更新时邮件推送。项目期间边学边写花了不少时间。\n项目结构 ├── config.json ├── go-teleplay.go ├── README.md ├── sitelib │ ├── btjiaSite.go │ ├── hao6vSite.go │ ├── siteInterface.go │ └── utils.go └── tmp 详细项目: https://github.com/zfj1441/go-teleplay\n编译路由器K3执行程序 windows下交叉编译:\nset GOOS=linux set GOARCH=arm go build go-teleplay.go GOOS：目标操作系统 GOARCH：目标操作系统的架构 OS ARCH OS version linux 386 / amd64 / arm \u0026gt;= Linux 2.6 darwin 386 / amd64 OS X (Snow Leopard + Lion) freebsd 386 / amd64 \u0026gt;= FreeBSD 7 windows 386 / amd64 \u0026gt;= Windows 2000 路由器程序部署 上传执行程序和配置文件文档结构如下\n├── go-teleplay ├── config.json └── tmp 增加定时任务\ncrontab -e */25 * * * * /root/go-teleplay \u0026gt;/tmp/go-teleplay.log 2\u0026gt;\u0026amp;1 一点点感想 相对python、java等跨平台，go编译程序基本上不需要环境的配置就能使用。有这么好的跨平台编译，以后自己用的windows工具统统都改CMD，GUI界面不适合我。\n","date":"2020-04-06T23:03:30+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T23:03:30+08:00","permalink":"https://zfj1441.com/p/%E8%BF%BD%E5%89%A7%E5%B7%A5%E5%85%B7go-teleplay/","title":"追剧工具go-Teleplay"},{"content":"前言 之前有过段时间在想淘汰的android机怎么废物利用起来，也搜索过类似的文章做闹钟、做相框、做网络视频监控。我觉得这都不能发挥一个手机的功能所以一直没着手。但最近我有了新的想法把废旧的android机做成短信服务器提供服务（类似于腾讯、阿里等的短信包）用于消息推送提醒。\n正文 原理说明 手机（android5.0及以上）上安装termux、termux-api （提供linux环境、发短信命令） 实现内网穿透，以达到外网控制手机。说说的内网穿透：作者有台腾讯服务器，使用rabbitmq实现。后来想想可以简单点是不是直接ssh代理就可以。 实现过程 开发环境 手机上安装termux 、termux-api（请使用 com.termux.api_0.31.apk版本由于google协议要求之后的版本去除发短信功能）。安装参考Termux 高级终端安装使用配置教程\n安装完成之后可以测试发短信命令\ntermux-sms-send -n 10010 helloworld python 环境安装 pkg install python pip install pika #rabbitmq python库 到此开发环境基本完成\n代码开发 手机端开发完成连接云服务器，监听命令 ##coding:utf-8 import pika import os credentials=pika.PlainCredentials(\u0026#39;用户名\u0026#39;, \u0026#39;密码\u0026#39;) parameters = pika.ConnectionParameters(host=\u0026#39;远端服务地址\u0026#39;, port=5672, virtual_host=\u0026#39;/\u0026#39;, credentials=credentials) connection = pika.BlockingConnection(parameters) channel = connection.channel() channel.queue_declare(queue=\u0026#39;hello\u0026#39;) def callback(ch, method, properties, body): \u0026#39;\u0026#39;\u0026#39;消息的回调方法\u0026#39;\u0026#39;\u0026#39; print(\u0026#34; [x] Received %r\u0026#34; % body) cmds = body.decode().split(\u0026#39;|\u0026#39;) #协议报文 if len(cmds)\u0026gt;0 and cmds[0]==\u0026#39;sms\u0026#39;: os.system(\u0026#39;termux-sms-send -n %s %s \u0026gt;/dev/null 2\u0026gt;\u0026amp;1\u0026#39; % (cmds[1], cmds[2])) # 发短信 elif len(cmds)\u0026gt;0 and cmds[0]==\u0026#39;call\u0026#39;: os.system(\u0026#39;termux-telephony-call %s \u0026gt;/dev/null 2\u0026gt;\u0026amp;1\u0026#39; % (cmds[1])) # 打电话 print(\u0026#34; [x] over\u0026#34;) channel.basic_consume(\u0026#39;hello\u0026#39;, callback, auto_ack=True) print(\u0026#39; [*] Waiting for messages. To exit press CTRL+C\u0026#39;) channel.start_consuming() 消息发送端开发 ## -*- coding: utf-8 -*- import pika import random class smsServer: \u0026#39;\u0026#39;\u0026#39;云端发送消息报文\u0026#39;\u0026#39;\u0026#39; def __init__(self): self.connection = pika.BlockingConnection(pika.ConnectionParameters(host=\u0026#39;localhost\u0026#39;)) self.channel = self.connection.channel() self.channel.queue_declare(queue=\u0026#39;hello\u0026#39;) def sms(self, tel, body): msg=\u0026#39;sms|\u0026#39;+str(tel)+\u0026#39;|\u0026#39;+body self.channel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=msg) print(\u0026#34; [x] Sent: %s\u0026#34; % msg) self.connection.close() def call(self, tel): msg=\u0026#39;call|\u0026#39;+str(tel) self.channel.basic_publish(exchange=\u0026#39;\u0026#39;, routing_key=\u0026#39;hello\u0026#39;, body=msg) print(\u0026#34; [x] Sent: %s\u0026#34; % msg) self.connection.close() if __name__==\u0026#39;__main__\u0026#39;: send = smsServer() ## send.sms(\u0026#39;10010\u0026#39;, \u0026#39;你好\u0026#39;) send.call(\u0026#39;10010\u0026#39;) 到此功能都以完成。\n其它 拨打电话接口termux-telephony-call只能拨打电话，但我并没有发现挂断电话的方法。比如发送keyevent或模拟触屏等。所以如果想实现电话炸弹那种接通自动挂，可以root手机后使用input命令（已经实现电话砸蛋功能，但过程有点复杂）。\n结尾 动手写之前也琢磨了很久，一篇好的文章怎么让读者看的舒服。想了很久依然没有找到答案，所以文章内容可能会让你一头雾水请多多包含。如有你有兴趣或有疑问可以微信公众号联系。\n","date":"2019-08-24T23:55:33+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:55:33+08:00","permalink":"https://zfj1441.com/p/%E5%BA%9F%E6%97%A7android%E6%89%8B%E6%9C%BA%E5%8F%98%E7%9F%AD%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%99%A8/","title":"废旧android手机变短信服务器"},{"content":"前言 4月换了份新工作。在新环境下要学习的地方很多；工作侧重点也有所改变。当然工作也更忙了。下班闲暇之余敲敲代码写写自己的程序也是一种享受IT职业的方式。 言归正传，换了新工作后我依然没有逃避txt转xlsx的问题，天天都在手动打开txt设值分隔符。某天终于按耐不住这重复无聊的步骤，于是基于《windows二维码工具3.0》写了这个小工具。\n正文 内容 txt 转 xlsx 网上源码真的很多 界面设计上，由于工作中需要一次转20多个txt，所以选择批处理方式。 合并单元格，日期格式，金额格式，小数点。xlsx格式这一直是头大的问题。但庆幸的是在我的工作中完全不需要设置xlsx格式，所以内容都是以“字符串”写入单元格，无合并单元格。 运行环境需要安装 Visual C++ Redistributable for Visual Studio 2015 安装包 演示 部分代码 class WriteThread(QtCore.QThread): \u0026#39;\u0026#39;\u0026#39; 工作线程，处理txt转xlsx \u0026#39;\u0026#39;\u0026#39; _signal = pyqtSignal(int, str) def __init__(self, srcfiles, dstfiles, split, coding): super(WriteThread, self).__init__() self.srcfiles = srcfiles self.dstfiles = dstfiles self.split = split self.coding = coding def run(self): currentfile = \u0026#39;\u0026#39; try: for i in range(len(self.srcfiles)): currentfile = self.srcfiles[i] self.txt_to_xlsx(self.srcfiles[i], self.dstfiles[i], self.split, self.coding) self._signal.emit(0, \u0026#34;OK\u0026#34;) except Exception as e: self._signal.emit(-1, currentfile + \u0026#39;:\u0026#39; + str(e)) def txt_to_xlsx(self, filename, outfile, split, encoding): fr = codecs.open(filename, \u0026#39;r\u0026#39;, encoding=encoding) wb = openpyxl.Workbook() ws = wb.active row = 0 for line in fr: row += 1 # line = line.strip() 2020年3月28号 line = line.split(split) col = 0 for j in range(len(line)): col += 1 ws.cell(column=col, row=row, value=line[j]) wb.save(outfile) 打包exe说明 环境使用virtualenv (annaconda环境下打包exe会非常的大) 安装pyqt5 openpyxl pyinstaller 打包命令 pyinstaller -F -w txt2xlsx.py 或 pyinstaller txt2xlsx.spec 感悟 实际工具不难写，开发初期写了个cmd版本，在工作中试用了一天。实测可以满足需求，后续花了几个小时完成了界面开发。小工具满足自己的需求，也给大家参考参考，最最重要的还是要想法去折腾。所以我也自己琢磨着搞个小程序，收集些需求空闲之余折腾下。\n源代码和工具下载 链接: https://pan.baidu.com/s/1bkITnqW85KNC0CSw52XuSQ 提取码: swca\n","date":"2019-06-27T23:51:58+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:51:58+08:00","permalink":"https://zfj1441.com/p/windows%E5%B0%8F%E5%B7%A5%E5%85%B7txt%E8%BD%ACxlsx/","title":"windows小工具txt转xlsx"},{"content":"问题描述 服务磁盘异常，更换后。pv vg lv一套下来。启动报系统文件异常。\n错误信息： filesystem. if the device is .... fsck.ext3: No such file or directory .... /dev目录无LVM创建的设备 处理过程 按系统提示，首先是怀疑LVM挂载的磁盘问题。修复模式下LVM一堆操作后 pvdisplay、vgdisplay、lvdisplay 都正常。也做mkfs.ext3操作。/dev 目录下也有相应的设备。然后重启后还是错。 fstab中移除挂载的LVM盘，系统能正常启动。 fdisk -l 显示 lvm磁盘。一直显示nvalid partition table（后面验证：正常服务器中同样显示） 关键点：系统访问文件系统但/dev下却没有设备。百度关键内容\u0026quot;suse vg 启动\u0026quot;找到关键性解决方案。 vg在系统启动中并没有激活。导致/etc/fstab无法挂载。\n解决方法 方法一：\ncd /etc/init.d/boot.d ln -s ../boot.lvm S05boot.lvm \u0026amp;\u0026amp; ln -s ../boot.lvm K05boot.lvm 方法二：\nchkconfig boot.lvm on 两种方法原理一样，都是设值lvm脚本开机启动\n总结 认真看日志找错误线索 百度、google （总有人和你一样倒霉） 参考 SUSE Linux重启后vg无法正常挂载问题解决 如何处理SUSE11虚拟机中手动创建逻辑卷并在fstab中设置成自动挂载后无法启动 LVM2 special device missing after reboot\n","date":"2019-05-15T23:50:05+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:50:05+08:00","permalink":"https://zfj1441.com/p/suse-linux-11-lvm%E7%A3%81%E7%9B%98%E6%8C%82%E8%BD%BD%E5%BC%82%E5%B8%B8%E6%97%A0%E6%B3%95%E5%90%AF%E5%8A%A8/","title":"SUSE Linux 11 LVM磁盘挂载异常无法启动"},{"content":"前言 最近用springboot做微信小程序后端。微信小程序后端服务只能使用443端口，导致应用只能使用root权限启动。做为长期linux工作者的我真忌讳使用root启动应用。所以花了点实现学习下应用生产部署。\n起初想简单点直接使用iptables的NAT转发，折腾了半天没搞定。后面使用nginx反向代理。 最后的部署图： 实施详情 iptables安装配置 禁用centos自带的firewall systemctl stop firewalld.service systemctl disable firewalld.service 安装iptables yum install iptables-services -y systemctl enable iptables systemctl start iptables 配置防火墙 # 后悔药 cp /etc/sysconfig/iptables /etc/sysconfig/iptables.`date +%Y%m%d` 编辑防火墙配置文件，添加放开80、443、3306 和ssh远程登录等端口\nvim /etc/sysconfig/iptables ##========================================================== *filter :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT -A INPUT -p icmp -j ACCEPT -A INPUT -i lo -j ACCEPT ##ssh服务端口，这不放开ssh就会登不上 -A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT ##自定义 -A INPUT -m state --state NEW -m tcp -p tcp --dport 80 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 443 -j ACCEPT -A INPUT -m state --state NEW -m tcp -p tcp --dport 3306 -j ACCEPT ##一定要放在这两行前面 -A INPUT -j REJECT --reject-with icmp-host-prohibited -A FORWARD -j REJECT --reject-with icmp-host-prohibited COMMIT ##================================================================= 生效配置 systemctl restart iptables.service #最后重启防火墙使配置生效 systemctl enable iptables.service #设置防火墙开机启动 nginx安装配置 添加nginx源 默认情况Centos7中无Nginx的源。 sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm 安装Nginx 通过yum search nginx看看是否已经添加源成功。如果成功则执行下列命令安装Nginx。 sudo yum install -y nginx 配置nginx ##万能后悔药 cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.`date +%Y%m%d` vi /etc/nginx/nginx.conf ##============================================= user xiaoming; #工作用户 worker_processes 1; error_log /home/xiaoming/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34;\u0026#39;; access_log /home/xiaoming/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; server {#80端口请求，直接跳转至443 listen 80; server_name www.xiaoming.com; rewrite ^(.*)$ https://${server_name}$1 permanent; } server {#证书配置 listen 443; ssl on; ssl_certificate /home/xiaoming/nginx.crt; ssl_certificate_key /home/xiaoming/nginx.key; access_log /home/xiaoming/log/nginx/service.log main; charset utf-8; location / {#所有443请求转发到127.0.0.1:8080 proxy_pass http://127.0.0.1:8080; } } } ##============================================= 启动Nginx并设置开机自动运行 sudo systemctl start nginx.service sudo systemctl enable nginx.service 说到这里分享个nginx部署权限 进程维度：\nnginx - root (master) - www-data (worker) uwsgi - root (emperor) - www-data (worker) 目录/文件维度：\nwww/ config/ log/ application/ socket/ uwsgi_sock nginx 和 uwsgi 都要以 root 权限启动，在配置文件中设置 worker 进程的用户 sock 文件 要 nginx_worker 可读 r 要 uwsgi_worker 可读可写 rx sock 文件所在目录 要 nginx_worker 可读 r 要 uwsgi_worker 可新建文件(可读可写) rx application 目录的所有者最好是 worker 进程的用户 静态目录/文件 要 nginx_worker 可读 r 所有目录/文件 要 uwsgi_worker 可读可写 rw log 目录/文件 nginx 和 uwsgi 都是以 root 身份写日志，日志文件的所有者是 root 应用的部署 上面nginx已经转发到127.0.0.1:8080端口，所以应用只需要监听8080端口。\njava -jar helloworld.jar \u0026gt;/dev/null \u0026amp; 一点点体会 起初觉得很简单，但真的动手还是遇到很多问题。比如iptables配置后无法访问，nginx没有转发到应用等等。所以再简单事也要自己动手试试，否则就只会眼高手低。\n","date":"2018-12-25T23:47:31+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:47:31+08:00","permalink":"https://zfj1441.com/p/%E4%B8%80%E6%AC%A1%E5%AE%8C%E6%95%B4%E7%9A%84iptables-nginx-springboot%E9%83%A8%E7%BD%B2/","title":"一次完整的iptables+nginx+springboot部署"},{"content":"前言 看了些Linux内存管理的文章，写一写加深对内存管理的印象。\n正文 程序的存储 ELF是Linux的主要可执行文件格式。ELF文件由4部分组成，分别是ELF头（ELF header）、程序头表（Program header table）、节（Section）和节头表（Section header table）。具体如下：\nProgram header描述的是一个段在文件中的位置、大小以及它被放进内存后所在的位置和大小。即要加载的信息； Sections保存着object 文件的信息，从连接角度看：包括指令，数据，符号表，重定位信息等等。在图中，我们可以看到Sections中包括： text 文本结 存放指令； rodata 数据结 readonly; data 数据结 可读可写； Section头表（section header table）包含了描述文件sections的信息。每个section在这个表中有一个入口；每个入口给出了该section的名字，大小，等等信息。相当于 索引！ 代码编译和存储 Linux 虚拟地址空间如何分布？ Linux 使用虚拟地址空间，大大增加了进程的寻址空间，由低地址到高地址分别为：\n只读段：该部分空间只能读，不可写；(包括：代码段、rodata 段(C常量字符串和#define定义的常量) ) 数据段：保存全局变量、静态变量的空间； 堆 ：就是平时所说的动态内存， malloc/new 大部分都来源于此。其中堆顶的位置可通过函数 brk 和 sbrk 进行动态调整。 文件映射区域：如动态库、共享内存等映射物理空间的内存，一般是 mmap 函数所分配的虚拟地址空间。 栈：用于维护函数调用的上下文空间，一般为 8M ，可通过 ulimit –s 查看。 内核虚拟空间：用户代码不可见的内存区域，由内核管理(页表就存放在内核虚拟空间)。 下图是 32 位系统典型的虚拟地址空间分布(来自《深入理解计算机系统》)。 char *a 与char a[] 的区别 char *d = \u0026ldquo;hello\u0026rdquo; 中的d是指向第一个字符‘h\u0026rsquo;的一个指针；char s[20] = \u0026ldquo;hello\u0026rdquo; 中数组名s也是指向第一个字符\u0026rsquo;h\u0026rsquo;的指针。现执行下列操作：strcat(d, s)。把字符串加到指针所指的字串上去，出现段错误。本质原因：*d=\u0026ldquo;hello\u0026quot;存放在常量区，是无法修的。而数组是存放在栈中，是可以修改的。 两者区别如下： 读写能力：char *a = \u0026ldquo;abcd\u0026quot;此时\u0026quot;abcd\u0026quot;存放在常量区。通过指针只可以访问字符串常量，而不可以改变它。而char a[20] = \u0026ldquo;abcd\u0026rdquo;; 此时 \u0026ldquo;abcd\u0026quot;存放在栈。可以通过指针去访问和修改数组内容。\n赋值时刻：char *a = \u0026ldquo;abcd\u0026quot;是在编译时就确定了（因为为常量）。而char a[20] = \u0026ldquo;abcd\u0026rdquo;; 在运行时确定\n存取效率：char *a = \u0026ldquo;abcd\u0026rdquo;; 存于静态存储区。在栈上的数组比指针所指向字符串快。因此慢，而char a[20] = \u0026ldquo;abcd\u0026quot;存于栈上，快。 另外注意：char a[] = \u0026ldquo;01234\u0026rdquo;，虽然没有指明字符串的长度，但是此时系统已经开好了，就是大小为6\u0026mdash;\u0026ndash;\u0026lsquo;0\u0026rsquo; \u0026lsquo;1\u0026rsquo; \u0026lsquo;2\u0026rsquo; \u0026lsquo;3\u0026rsquo; \u0026lsquo;4\u0026rsquo; \u0026lsquo;5\u0026rsquo; \u0026lsquo;\\0\u0026rsquo;，(注意strlen(a)是不计\u0026rsquo;\\0\u0026rsquo;)\n结束 初步了解了程序的存储结构和运行结构。分享给和我一样还不太了解的朋友。一步一个脚印 . . .\n参考: linux环境内存分配原理 mallocinfo Linux系统内存管理 Linux内存管理（最透彻的一篇） Linux C 内存管理 深入理解C语言内存管理\n","date":"2018-11-06T23:43:47+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:43:47+08:00","permalink":"https://zfj1441.com/p/linux%E7%A8%8B%E5%BA%8F%E5%AD%98%E5%82%A8%E6%97%B6%E5%92%8C%E8%BF%90%E8%A1%8C%E6%97%B6%E7%9A%84%E7%BB%93%E6%9E%84/","title":"linux程序存储时和运行时的结构"},{"content":"前言 平时在一直在linux/unix下工作，早已经习惯使用vim编辑文本。在windows虽然也有众多的编辑器，但用的不顺溜。所以windows下也需要个vim。\ngvim安装 安装gvim 运行下载的安装程序，并按照说明一步一步正确安装即可 我这里安装的路径是 D:\\Program Files (x86)\\Vim\n简要配置 配置环境变量 配置vim参数 在D:\\Program Files (x86)\\Vim\\_vimrc最后加入 colorscheme desert \u0026#34;配色方案 syntax enable \u0026#34;打开语法高亮 syntax on \u0026#34;打开语法高亮 gvim默认Ctrl+f是弹出搜索，而linux下Ctrl+f是下翻。按下面方法修改 修改D:\\Program Files (x86)\\Vim\\vim80\\mswin.vim注释下面代码\n\u0026#34;if has(\u0026#34;gui\u0026#34;) \u0026#34; \u0026#34; CTRL-F is the search dialog \u0026#34; noremap \u0026lt;C-F\u0026gt; :promptfind\u0026lt;CR\u0026gt; \u0026#34; inoremap \u0026lt;C-F\u0026gt; \u0026lt;C-\\\u0026gt;\u0026lt;C-O\u0026gt;:promptfind\u0026lt;CR\u0026gt; \u0026#34; cnoremap \u0026lt;C-F\u0026gt; \u0026lt;C-\\\u0026gt;\u0026lt;C-C\u0026gt;:promptfind\u0026lt;CR\u0026gt; \u0026#34; \u0026#34; \u0026#34; CTRL-H is the replace dialog \u0026#34; noremap \u0026lt;C-H\u0026gt; :promptrepl\u0026lt;CR\u0026gt; \u0026#34; inoremap \u0026lt;C-H\u0026gt; \u0026lt;C-\\\u0026gt;\u0026lt;C-O\u0026gt;:promptrepl\u0026lt;CR\u0026gt; \u0026#34; cnoremap \u0026lt;C-H\u0026gt; \u0026lt;C-\\\u0026gt;\u0026lt;C-C\u0026gt;:promptrepl\u0026lt;CR\u0026gt; \u0026#34;endif 更多详细配置参考\n技巧分享 删除空行 :g/^$/d(\u0026rsquo;d\u0026rsquo;后面有个空格) 删除空行以及只有空格的行 :g/^\\s*$/d(\u0026rsquo;d\u0026rsquo;后面有个空格) 删除/* */注释 :%s!\\s*/\\*\\_.\\{-}\\*/\\s*! !g 去掉所有的“//”注释 :%s!\\s*//.*!! 转成十六进制显示 :%!xxd 十六进制转回正常显示 :%!xxd -r 更多技巧请看vim 正则表达式 ","date":"2018-09-30T23:40:50+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:40:50+08:00","permalink":"https://zfj1441.com/p/windows%E4%B8%8Bgvim%E7%AE%80%E8%A6%81%E9%85%8D%E7%BD%AE/","title":"windows下gvim简要配置"},{"content":"前言 众所周知百度云盘的东西上传容易下载难。如果上传的速度是高铁，那下载的速度就是自行车。如果你也有同样的感受，今天小7就还你体验下“超车的快感”。\n正文 首先介绍下今天的主角：油猴Tampermonkey\nTampermonkey是一款免费的浏览器扩展程序。利用它我们安装大神分享的实用脚本，实现迅雷下载百度资源；各大视频网站资源下载；视频去广告等等\ntampermonkey 安装 这里以chrome为例。其他浏览器比较简单（直接去应用商店里搜索\u0026quot;tampermonkey\u0026quot;或“油猴”）\n下载 tampermonkey安装包（末尾有资源）。 在chrome中，在地址栏输入：chrome://extensions/ 回车，打开应用管理器，可以看到之前安装的其他应用。 拖放刚刚的tampermonkey安装包到界面里，松开鼠标，会弹出安装提示，点击确定安装就可以了 百度云盘脚本程序下载 进入地址https://greasyfork.org/zh-CN搜索百度,选择“百度网盘直接下载助手” 使用 进入百度资源，选中后会发现“下载助手按钮”，选择“显示链接”。复制链接就可以去迅雷或其他下载工具中快速下载，从而突破百度网盘限速。 最后 tampermonkey 插件有很多，百度网盘下载工具只是其中一个，另外一个我比较常用的是视频VIP破解。这个推荐希望你能满意。\n链接:https://pan.baidu.com/s/10x287Cs6JicurCHOX1FOcQ 提取码:5gcy\n","date":"2018-09-20T12:46:13+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T12:46:13+08:00","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8%E6%B2%B9%E7%8C%B4%E6%8F%92%E4%BB%B6%E4%B8%8B%E8%BD%BD%E7%99%BE%E5%BA%A6%E7%BD%91%E7%9B%98%E8%B5%84%E6%BA%90/","title":"使用油猴插件下载百度网盘资源"},{"content":"前言 本篇要从小7的朋友说起。数据组的他收到业务提交的三个excel。要求将其中的地址转成经纬度，然后匹配最近的银行网点。现用的转换程序约二秒出一条记录，三个excel总数据量约十几万。这也难怪会愁了。\n正文 0x01使用scrapy爬虫框架处理。 原因有以下几点：\n上次使用scrapy爬携程评论现在还是比较熟悉 scrapy使用的是异步请求（重点） 框架设计有数据持久化 但在程序中，速度任然很慢。没去深究原因，转面使用aiohttp+asyncio处理。 0x02试用asyncio asyncio是Python 3.4版本引入的标准库，直接内置了对异步IO的支持。 asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用，然后把需要执行的协程扔到EventLoop中执行，就实现了异步IO\nasyncio代码，40个并发跑起真的非常快。7万的数据，半个小时搞定。\nimport time import asyncio from aiohttp import ClientSession i = 0 max = 40 addrs = [] tasks = [] url = \u0026#34;http://api.map.baidu.com/geocoder/v2/?address={}\u0026amp;output=json\u0026amp;ak=你的百度AK\u0026#34; async def getInfo(url): async with ClientSession() as session: async with session.get(url) as response: return await response.read() def run(): #读取地址文件 with open(\u0026#39;a.txt\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf8\u0026#39;) as f: lines = f.readlines() for line in lines: line = line.strip(\u0026#39;\\n\u0026#39;) task = asyncio.ensure_future(getInfo(url.format(line))) tasks.append(task) addrs.append(line) global i, max i = i + 1 if i \u0026gt;= max: # 40个一并发 t = 0 results = loop.run_until_complete(asyncio.gather(*tasks)) #写入响应信息 with open(\u0026#39;a_dst.txt\u0026#39;, \u0026#39;a\u0026#39;, encoding=\u0026#39;utf8\u0026#39;) as dstfile: for r in results: dstfile.writelines(addrs[t] + r.decode() + \u0026#39;\\n\u0026#39;) t = t + 1 i = 0 tasks.clear() addrs.clear() if __name__ == \u0026#39;__main__\u0026#39;: loop = asyncio.get_event_loop() run() 0x03处理后续结果 处理下结果文件，最后转成excel，十几万的数据处理完成。\n结束语 使用asyncio上手还是比较简单。主要是理解事件循环，协程和任务，future的关系。\n参考： Python黑魔法 \u0026mdash; 异步IO（ asyncio） 协程 python异步编程之asyncio（百万并发）\n","date":"2018-09-08T23:38:42+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:38:42+08:00","permalink":"https://zfj1441.com/p/%E5%88%9D%E8%AF%95python%E9%BB%91%E9%AD%94%E6%B3%95asyncio/","title":"初试python黑魔法asyncio"},{"content":"前言 当我们使用电脑试图去打开某个文件的时候，你总会先想这个文件在哪个盘在哪个目录下。然后双击一层层的进直到找到目标文件。这样频繁的文件夹点击将会极大的制约你的效率。Listary就能帮你解决上述问题，当然它的强大之处不仅限于此。\n正文 维基百科对解释如下：\nListary是一款用于Windows的文件名定位/搜索辅助软件。它为Windows传统低效的文件打开/保存对话框提供了便捷、人性化的文件（夹）定位方式[5]，同时改善了常见文件管理器中文件夹切换的效率。\n维基百科解释太深奥？那看看网友的解释：\nListary 是 windows 下一个可以快速搜索所有程序，文件，并且可以快速启动程序和打开相应文件的工具。\n古人有云百闻不如一见，那先看看Listary的使用\nListary简单使用介绍 1. 列表快速索引 如：我下载文件夹里面有很多文件和文件夹，打开后，我要打开我之前的下的电影《从你的全世界路过》，我只需要输入文件名的首字母“cndqsjlg”或者输入部分的文件名“qsj”（全世界）再或者“cnsj”（从你世界），都是可以搜索到该文件的，然后选中，回车即可打开。 2. 高效快速启动程序和搜索文件 快：它自身启动快，唤醒方便（快捷键），窗口简洁，关键是搜索的速度嗖嗖地。 快速唤出搜索框：ALT + Q。快速隐藏搜索框：再按一次 ALT+Q。 准：对于程序或者文件名的搜索是支持 首字母匹配：wyyyy - (网易云音乐)） 3. 强大的Web功能 一般搜索：无论是谷歌，百度，必应，雅虎还是三六零，弹指间搜你所想知 如：听说有一款手机叫锤子，我来查查看。 我要搜索 锤子 百度搜索：bd + 空格 + 锤子 更多详细介绍 [Listary]好用到哭的快速搜索工具\n网友使用感想 使用Listary前 使用Listary后 结语 Listary简单易上手，但要用的6的起飞还得和我一样好好看视频认真学习下Listary的技巧。\n","date":"2018-08-15T23:21:10+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:21:10+08:00","permalink":"https://zfj1441.com/p/listary%E4%B8%80%E6%AC%BE%E4%B8%8D%E5%8F%AA%E6%98%AF%E7%A8%8B%E5%BA%8F%E5%91%98%E9%9C%80%E8%A6%81%E7%9A%84%E8%BD%AF%E4%BB%B6/","title":"Listary一款不只是程序员需要的软件"},{"content":"前言 自己写爬虫算下来多少也有那么些了，但基本上都是爬的小数据完全不用考虑效率。但今天小J的朋友让我帮忙爬下全国XXX酒店信息。这不得去考虑爬虫效率，以及反反爬虫技巧。此时我脑海里第一想到了scrapy框架。今天小试scrapy，爬下自己的博客统计下文章的浏览量。\n正文 0x01 scrapy安装 建立虚拟python3.6环境 conda create --name python3.6 python=3.6 安装scrapy activate python3.6 conda install scrapy 0x02 项目 建立scrapy项目 scrapy startproject csdn 使用pycharm打开项目并创建csdn.py import scrapy class csdnSpider(scrapy.Spider): name = \u0026#39;csdn\u0026#39; #文章分页url start_urls = [ \u0026#39;https://blog.csdn.net/vr7jj/article/list/1\u0026#39;, \u0026#39;https://blog.csdn.net/vr7jj/article/list/2\u0026#39; ] def parse(self, response): print(\u0026#39;#-----------------------\u0026#39;) li = response.css(\u0026#39;#mainBox \u0026gt; main \u0026gt; div.article-list \u0026gt; div \u0026gt; h4 \u0026gt; a\u0026#39;) for i in li: # print(i) title = str(i.css(\u0026#39;::text\u0026#39;).extract()[2]).strip() link = i.css(\u0026#39;::attr(href)\u0026#39;).extract()[0] print(\u0026#39;next --\u0026gt; [%s][%s]\u0026#39; % (link,title)) yield scrapy.Request(link,callback=self.parse_item, meta={\u0026#39;title\u0026#39;: title}) # print(i.css(\u0026#39;attr(href)\u0026#39;).extract()) print(\u0026#39;#-----------------------\u0026#39;) def parse_item(self, response): title = response.meta[\u0026#39;title\u0026#39;] print(\u0026#39;******%s*******\u0026#39; % title, end=\u0026#39;\u0026#39;) con = response.css(\u0026#39;#mainBox \u0026gt; main \u0026gt; div.blog-content-box \u0026gt; div.article-info-box \u0026gt; div \u0026gt; div \u0026gt; span::text\u0026#39;) print(con.extract()[0], end=\u0026#39;\u0026#39;) print(\u0026#39;************************\u0026#39;) 创建begin.py from scrapy import cmdline cmdline.execute(\u0026#34;scrapy crawl csdn\u0026#34;.split()) 运行begin.py 感想 scrapy功能很强大，认识有限。所以目前留下很多问题。\npost请求方式如何去爬取 需要登陆scrapy有什么处理方式 如何持久化 反反爬虫怎么实现 参考内容 Scrapy框架\nScrapy 1.5 documentation\nScrapy 0.24 中文文档\nscrapy实现递归爬取\nscrapy如何迭代爬去？\n","date":"2018-08-04T23:18:49+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:18:49+08:00","permalink":"https://zfj1441.com/p/python%E4%BD%BF%E7%94%A8scrapy%E6%A1%86%E6%9E%B6%E6%8A%93%E5%8F%96csdn%E6%96%87%E7%AB%A0%E9%98%85%E8%AF%BB%E9%87%8F/","title":"Python使用scrapy框架抓取CSDN文章阅读量"},{"content":"前言 最近老婆开了家淘宝店。为了支持她的店铺，主动帮忙“装修“店铺；手把手教PS做图。但对于新店来说这都不够，最后还是得靠亲朋好友来刷单引流量。单刷完评价成了问题，如何写出吸引买家的评价。想了想还是用python爬同类店铺的评价来的快。\n正文 0x01 快速入手，直接baidu“python淘宝评价\u0026quot; 果然一堆。打开pycharm直接运行。效果很是理想。 0x02 专业好评价怎么只能光有文字没有图片呢，所以详细分析下评价的json内容。找到评价中的图片url下载图片。 def getimge(url, filepath): \u0026#39;\u0026#39;\u0026#39; 下载图片 :param url: :param filepath: :return: \u0026#39;\u0026#39;\u0026#39; ir = requests.get(url) if ir.status_code == 200: with open(filepath, \u0026#39;wb\u0026#39;) as f: f.write(ir.content) else: raise Exception(\u0026#34;通信异常\u0026#34;) 0x03 问题汇总： 1、将评论写入文件中时报如下错误 应该是utf8转gbk时，没有找到对就的gbk文字。最后使用中文范围更大的gb18030顺利解决。\nwith open(os.path.join(product_dir, \u0026#34;content.txt\u0026#34;), \u0026#34;a\u0026#34;, encoding=\u0026#39;gb18030\u0026#39;) as f: f.write(str(count + 1) + \u0026#39;:\u0026#39; + j[\u0026#39;content\u0026#39;] + \u0026#39;\\n\u0026#39;) 效果 源码 链接: https://pan.baidu.com/s/1sADX8LQgNCe8Na3neU9wYg 密码: iwjc\n广告 【广州威蒂娜品牌店】，復·制这段描述€kmMQb0VNFEr€后咑閞手机淘宝或者用浏览器咑閞http://m.tb.cn/h.32gDlKv查看\n","date":"2018-07-22T23:10:59+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:10:59+08:00","permalink":"https://zfj1441.com/p/python%E7%88%AC%E8%99%AB%E8%8E%B7%E5%BE%97%E6%B7%98%E5%AE%9D%E5%95%86%E5%93%81%E8%AF%84%E8%AE%BA/","title":"Python爬虫获得淘宝商品评论"},{"content":"工具说明 先看下最新版本工具界面 本次升级优化如下内容：\n增加解二维码功能 “二维码”和“logo文件”可直接拖进图片 源码解析 python二维码解析库常用的有zbar、zbarlight、zxing\n1、zbar和zbarlight内核一致，都是基于zbar的dll编译加载的。 2、zbarlight使用比zbar更简单，不过是在zbar的基础又做了一点点封装而已。 3、zxing是基于java的zxing核心的python分支，其原理是调用javaw 加载zxing的core.jar包，再获取输出结果。\n但zbar不支持python3，zbarlight也是同理。而zxing使用还得去调用java有些麻烦。之后在github找到了python3的库pyzbar\ndef decodeQr(filename): from pyzbar.pyzbar import decode if os.path.exists(filename): d = decode(Image.open(filename)) return d[0].data else: return \u0026#39;\u0026#39; pyqt5 输入控拖拽功能的实现\nclass LineEditEx(QtWidgets.QLineEdit): \u0026#39;\u0026#39;\u0026#39;重写QLineEdit\u0026#39;\u0026#39;\u0026#39; def __init__(self, parent=None): super().__init__(None, parent) self.setGeometry(50, 50, 100, 20) self.setAcceptDrops(True) self.setDragEnabled(True) # 开启可拖放事件 def dragEnterEvent(self, QDragEnterEvent): e = QDragEnterEvent if e.mimeData().hasText(): e.accept() else: e.ignore() def dropEvent(self, e): file = str(e.mimeData().text()).split(\u0026#39;file:///\u0026#39;)[1] self.setText(file) 源码及工具 链接:https://pan.baidu.com/s/1rcAR-JpRcSb1uR3MdpqCTQ 密码:bu5q\n","date":"2018-06-24T23:08:17+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:08:17+08:00","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8pyqt5%E5%86%99%E4%B8%AAwindows%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%B7%A5%E5%85%B73.0/","title":"使用pyqt5写个windows二维码工具3.0"},{"content":"前言 使用pyqt5写个windows二维码工具完成了基本的二维码生成。本篇完善了下功能。\n新工具界面如下： 正文 升级内容 二维码中嵌入logo 二维码在界面实时显示 界面调整 功能实现 pyqt5创建pushButton并设置按钮响应函数，创建label用于显示二维码\ndef initUI(self): #略去多行..... # 创建一个PushButton并设置点击弹出文件选择框 la1 = QtWidgets.QLabel(\u0026#39;logo文件\u0026#39;) la2 = QtWidgets.QLabel(\u0026#39;数据内容\u0026#39;) self.logofile = QtWidgets.QLineEdit() self.selectButton = QtWidgets.QPushButton(\u0026#34;选择\u0026#34;) self.selectButton.clicked.connect(self.doSelectFile) #创建一个label显示生成的二维码 self.label = QtWidgets.QLabel() self.label.setFixedSize(300, 300) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setText(\u0026#34;二维显示区\u0026#34;) palette = QPalette() palette.setColor(QPalette.Window, QtCore.Qt.white) self.label.setPalette(palette) self.label.setAutoFillBackground(True) #略去多行..... def doSelectFile(self): \u0026#39;\u0026#39;\u0026#39; 选择logo图片路径 :return: \u0026#39;\u0026#39;\u0026#39; filename, _ = QFileDialog.getOpenFileName(self, \u0026#39;请选择logo图片\u0026#39;, os.getcwd(), \u0026#34;PNG files(*.png)\u0026#34;); if filename != \u0026#39;\u0026#39;: self.logofile.setText(filename) else: self.logofile.setText(\u0026#39;\u0026#39;) 结束 功能实现还是很简单的，主要还是学习为主。欢迎各位留言讨论新功能\n源码及程序 链接:https://pan.baidu.com/s/1rcAR-JpRcSb1uR3MdpqCTQ 密码:bu5q\n","date":"2018-06-11T23:05:57+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:05:57+08:00","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8pyqt5%E5%86%99%E4%B8%AAwindows%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%B7%A5%E5%85%B72.0/","title":"使用pyqt5写个windows二维码工具2.0"},{"content":"前言 最近看到有网友爬《王者荣耀》全英雄皮肤图，觉得挺有意思的；也顺便记录下自己在分析爬虫的过程。\n正文 0x01分析 首先打开http://pvp.qq.com/web201605/herolist.shtml可以看到全英雄列表，F12进入调试模式。找到英雄对应的链接地址。 然后点进英雄后F12，选择对应的皮肤查询图片链接地址。这里取图片链接有有问题，后面代码实现详细说。 最后就是搜集链接地址，下载图片保存。\n0x02代码实现 使用requests库抓取页面，使用BeautifulSoup分析返回的html。获取所有英雄的详细链接、名称和编号\nimport os import re import requests from bs4 import BeautifulSoup baseurl = \u0026#39;http://pvp.qq.com/web201605\u0026#39; mainurl = \u0026#39;http://pvp.qq.com/web201605/herolist.shtml\u0026#39; herolist = [] def getHeroList(): \u0026#39;\u0026#39;\u0026#39;取所以英雄存入list中\u0026#39;\u0026#39;\u0026#39; hero = {} res = requests.get(mainurl) sp = BeautifulSoup(res.content, \u0026#34;html.parser\u0026#34;) lists = sp.select(\u0026#39;body \u0026gt; div.wrapper \u0026gt; div \u0026gt; div \u0026gt; div.herolist-box \u0026gt; div.herolist-content \u0026gt; ul \u0026gt; li\u0026#39;) for li in lists: oj = li.select(\u0026#39;a\u0026#39;)[0]; hero[\u0026#39;url\u0026#39;] = oj[\u0026#39;href\u0026#39;] hero[\u0026#39;name\u0026#39;] = oj.text # 正则表达式取ename编号 ename = re.findall(\u0026#39;herodetail/(\\d+)\\.shtml\u0026#39;, oj[\u0026#39;href\u0026#39;])[0] hero[\u0026#39;ename\u0026#39;] = ename herolist.append(hero) hero = {} return herolist 获取英雄皮肤图片链接，本以为会像“获取英雄链接”一样简单的，但我错了。通过代码获取的\u0026quot;英雄详细页“数据 和 浏览器F12看到的不一样。 代码获取的内容 浏览器F12下的内容 0x03问题分析与处理 猜想一 我首先想到的是cookies 和 headers 参数。但后面详细分析请求和几次尝试后问题还是没能解决。\n猜想二 怀疑是页面ajax异步请求获取的地址。但分析后并未找至ajax请求数据。\n问题确定 后来详细看了几个 js文件才找至答案。图片链接是js中生成。 0x04爬虫的实现 for hero in herolist: herodir = os.path.join(os.getcwd(), hero[\u0026#39;name\u0026#39;]) heropage = baseurl + \u0026#39;/\u0026#39; + hero[\u0026#39;url\u0026#39;] print(\u0026#39;------------%s---%s-----------\u0026#39; % (herodir, heropage)) res = requests.get(heropage) sop = BeautifulSoup(res.content, \u0026#34;html.parser\u0026#34;) li = sop.select(\u0026#39;body \u0026gt; div.wrapper \u0026gt; div.zk-con1.zk-con \u0026gt; div \u0026gt; div \u0026gt; div.pic-pf \u0026gt; ul \u0026#39;)[0][\u0026#39;data-imgname\u0026#39;] print(li) li = str(li).split(\u0026#39;|\u0026#39;) print(li) # 遍历所有皮肤 for i in range(len(li)): # 生成图片链接 imgurl = \u0026#39;http://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/\u0026#39; \\ + hero[\u0026#39;ename\u0026#39;] + \u0026#39;/\u0026#39; + hero[\u0026#39;ename\u0026#39;] + \u0026#39;-bigskin-\u0026#39; + str(i + 1) + \u0026#39;.jpg\u0026#39; imgname = os.path.join(herodir, li[i]+\u0026#34;.jpg\u0026#34;) print(\u0026#39;----[%s]--[%s]---\u0026#39; % (imgname, imgurl)) # 创建英雄目录 if os.path.exists(herodir) == False: os.mkdir(herodir) saveImg(imgname, imgurl) ====== 20181024更新 ===== 有朋友反应已经不能正常爬取图片，今天维护下。发现页面中的herolist已经改成ajax异步获取。同步下代码给有需要的朋友 两图说明下ajax的过程 环境说明 python3.6 requests bs4 json\n最新代码 链接: https://pan.baidu.com/s/17FP1jom16VvXHc0AJkkIGA 密码: 5i5u\n","date":"2018-06-07T12:37:16+08:00","image":"https://picsum.photos/800/600?r=2024-05-07T12:37:16+08:00","permalink":"https://zfj1441.com/p/%E8%8E%B7%E5%8F%96%E7%8E%8B%E8%80%85%E8%8D%A3%E8%80%80%E5%85%A8%E8%8B%B1%E9%9B%84%E9%AB%98%E6%B8%85%E6%97%A0%E7%A0%81%E5%9B%BE/","title":"获取《王者荣耀》全英雄高清无码图"},{"content":"前言 最近在维护自己开设的公众号，偶尔需要生成二维码。二维码生成很多但大多都是网页版本。python生成二维码之前也实现过，所以有空闲自己使用pyqt5写个windows工具。\n正文 将数据生成二维 这里有个坑qrcode模块需要image支持。测试过程中因为没有安装image模块，一调试程序就退出。纠结了大半个小时\nimport qrcode def createQR(data, filename=\u0026#39;\u0026#39;): fn = \u0026#39;\u0026#39; if filename == \u0026#39;\u0026#39;: fn = os.path.join(os.getcwd(), \u0026#39;qrcode_tmp.png\u0026#39;) else: fn = os.path.join(os.getcwd(), filename) img = qrcode.make(data) # img.get_image().show()显示 img.save(fn) return fn pyqt5前端界面 一个输入框获取用户需要转二维码的数据，一个按钮执行转换，很简单。（网上现学现卖）\nclass MyView(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): # 设置窗口的位置和大小 self.setGeometry(300, 300, 300, 220) # 设置窗口的标题 self.setWindowTitle(\u0026#39;Icon\u0026#39;) self.setWindowIcon(QIcon(\u0026#39;web.png\u0026#39;)) # 创建一个okButton并设置响应函数 self.okButton = QPushButton(\u0026#34;action\u0026#34;) self.okButton.clicked.connect(self.doAction) self.qline = QLineEdit(self) vbox = QVBoxLayout() vbox.addWidget(self.qline) vbox.addWidget(self.okButton) self.setLayout(vbox) self.show() def doAction(self): data = self.qline.text() fn = createQR(data) QMessageBox.information(self, \u0026#34;二维码生成\u0026#34;, fn,QMessageBox.Yes) if __name__ == \u0026#39;__main__\u0026#39;: # 创建应用程序和对象 app = QApplication(sys.argv) ex = MyView() sys.exit(app.exec_()) pyinstaller打包成exe程序 pyinstaller -F -w windows_qrcode.py windows二维码工具就完成了。 前后1个半小时（pyqt5界面花了点时间，qrcode坑捣鼓了半个小时)。代码很粗糙有兴趣的朋友可以继续完善，比如直接在前端界面显示二维码，或加入logo等等。\n完整源码 https://download.csdn.net/download/vr7jj/10458583\n","date":"2018-06-04T23:04:10+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T23:04:10+08:00","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8pyqt5%E5%86%99%E4%B8%AAwindows%E4%BA%8C%E7%BB%B4%E7%A0%81%E5%B7%A5%E5%85%B7/","title":"使用pyqt5写个windows二维码工具"},{"content":"使用centos7编译baidupcs出现如下错误 CDPATH=\u0026#34;${ZSH_VERSION+.}:\u0026#34; \u0026amp;\u0026amp; cd . \u0026amp;\u0026amp; /bin/sh /root/BaiduPCS/build-aux/missing aclocal-1.15 -I m4 /root/BaiduPCS/build-aux/missing: line 81: aclocal-1.15: command not found WARNING: \u0026#39;aclocal-1.15\u0026#39; is missing on your system. You should only need it if you modified \u0026#39;acinclude.m4\u0026#39; or \u0026#39;configure.ac\u0026#39; or m4 files included by \u0026#39;configure.ac\u0026#39;. The \u0026#39;aclocal\u0026#39; program is part of the GNU Automake package: \u0026lt;http://www.gnu.org/software/automake\u0026gt; It also requires GNU Autoconf, GNU m4 and Perl in order to run: \u0026lt;http://www.gnu.org/software/autoconf\u0026gt; \u0026lt;http://www.gnu.org/software/m4/\u0026gt; \u0026lt;http://www.perl.org/\u0026gt; make: *** [aclocal.m4] Error 127 先执行 autoreconf -ivf 然后再make，问题就解约了。 参考： csdn博客 在CentOS 7和Ubuntu 14.03上安装Build Essentials\n","date":"2018-05-24T22:57:33+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:57:33+08:00","permalink":"https://zfj1441.com/p/centos7%E7%BC%96%E8%AF%91%E9%97%AE%E9%A2%98aclocal-1.14-commands-not-found/","title":"Centos7编译问题：aclocal 1.14: commands not found"},{"content":"前言 玩esp-01有断时间了，自己使用homeassistant搭建后台控制中心，esp-01做终端控制灯、控制风扇、温度检测等等。但在控制之前esp-01需要配网，说到配网baidu搜索到最多应该就是smartconfig，以及微信的airkiss。但是不管是smartconfi或airkiss配置网，配置过程很不稳定，经常无法配置成功然后就只能断电重来；另一个原因是smartconfig需要app上配置很是麻烦。以上原因让我去寻找找到了另一种配网方式webconfig\nwebconfig基本思路 基本思路是ESP8266工作AP模式下，作为TCP Server监听TCP Client的连接。因为网页HTTP默认的端口是80，所以ESP8266作为TCP Server的端口需要设置为80。电脑连接上ESP8266的AP后，网页访问默认IP地址192.168.4.1，此时ESP8266就会收到来自网页的HTTP的Get请求,此请求数据为HTML格式。ESP8266收到请求后，读出保存在Flash中的HTML格式网页，并将带有HTML应答头的HTML网页发送给网页，网页端就可以显示出网页。\n实现过程 从flash中读取上次保留的wifi信息，若有wifi信息进入station模式反之进程softap模式等待配网 void ICACHE_FLASH_ATTR init_done_cb_init(void) { struct station_config stconfig; struct softap_config apconfig; print_chip_info(); os_memset(\u0026amp;stconfig, 0, sizeof(struct station_config)); wifi_station_get_config_default(\u0026amp;stconfig); if(os_strlen(stconfig.ssid) != 0){ os_printf(\u0026#34;ssid[%s]pass[%s]\u0026#34;, stconfig.ssid, stconfig.password); wifi_set_opmode(STATION_MODE); wifi_station_set_reconnect_policy(TRUE); wifi_station_set_auto_connect(TRUE); } else{ wifi_set_opmode(SOFTAP_MODE); wifi_softap_get_config(\u0026amp;apconfig); os_memset(apconfig.ssid, 0, 32); os_memset(apconfig.password, 0, 64); os_sprintf(apconfig.ssid,\u0026#34;ESP_%X\u0026#34;,system_get_chip_id()); apconfig.authmode = AUTH_OPEN; apconfig.ssid_len = 0; apconfig.max_connection = 5; wifi_softap_set_config(\u0026amp;apconfig); wifi_station_set_reconnect_policy(FALSE); wifi_station_set_auto_connect(FALSE); wifi_station_disconnect(); } #if PLUG_DEVICE user_plug_init(); #elif FAN_DEVICE user_fan_init(); #elif DS18B20_DEVICE ds_init(); #endif user_webserver_init(SERVER_PORT); } 在TCP Server接收函数中实现解析GET请求获取配网页面，POST请求配置 #define HTML\t\u0026#34;\u0026lt;html\u0026gt;\u0026lt;head\u0026gt;\u0026lt;meta name=\\\u0026#34;viewport\\\u0026#34; content=\\\u0026#34;width=device-width, initial-scale=1\\\u0026#34;/\u0026gt;\u0026lt;/head\u0026gt;\u0026#34;\t\\ \u0026#34;\u0026lt;body align=\\\u0026#34;center\\\u0026#34;\u0026gt;\u0026lt;h2\u0026gt;WiFi Settings\u0026lt;/h2\u0026gt;\u0026lt;form method=\u0026#39;post\u0026#39;\u0026gt;\u0026#34; \\ \u0026#34;\u0026lt;input type=\u0026#39;text\u0026#39; name=\u0026#39;ssid\u0026#39; placeholder=\u0026#39;ssid\u0026#39;\u0026gt;\u0026lt;br\u0026gt;\u0026#34;\t\\ \u0026#34;\u0026lt;input type=\u0026#39;text\u0026#39; name=\u0026#39;pass\u0026#39; placeholder=\u0026#39;pass\u0026#39;\u0026gt;\u0026lt;br\u0026gt;\u0026#34; \\ \u0026#34;\u0026lt;input type=\u0026#39;submit\u0026#39;\u0026gt;\u0026lt;br\u0026gt;\u0026lt;/form\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt;\u0026#34; LOCAL void ICACHE_FLASH_ATTR webconfig_set_wifi(char* urlparam){ char *p = NULL, *q = NULL; char ssid[10], pass[15]; os_memset(ssid, 0, sizeof(ssid)); os_memset(pass, 0, sizeof(pass)); p = (char *)os_strstr(urlparam, \u0026#34;ssid=\u0026#34;); q = (char *)os_strstr(urlparam, \u0026#34;pass=\u0026#34;); if ( p == NULL || q == NULL ){ return; } os_memcpy(ssid, p + 5, q - p - 6); os_memcpy(pass, q + 5, os_strlen(urlparam) - (q - urlparam) - 5); os_printf(\u0026#34;ssid[%s]pass[%s]\\r\\n\u0026#34;, ssid, pass); wifi_set_opmode(STATION_MODE); struct station_config stConf; stConf.bssid_set = 0; os_memset(\u0026amp;stConf.ssid, 0, sizeof(stConf.ssid)); os_memset(\u0026amp;stConf.password, 0, sizeof(stConf.password)); os_memcpy(\u0026amp;stConf.ssid, ssid, os_strlen(ssid)); os_memcpy(\u0026amp;stConf.password, pass, os_strlen(pass)); wifi_station_set_config(\u0026amp;stConf); //重启 system_restart(); return ; } LOCAL void ICACHE_FLASH_ATTR webserver_recv(void *arg, char *pusrdata, unsigned short length){ struct espconn *ptrespconn = arg; URL_Frame *pURL_Frame = NULL; char *pParseBuffer = NULL; char szOut[512]; int stat = -1; static char post_data[1024]; static bool last_post = false; os_printf(\u0026#34;length[%d]\\r\\n\u0026#34;, length); os_printf(\u0026#34;recv[%s]\\r\\n\u0026#34;, pusrdata); if(check_data(pusrdata, length) == false){ os_printf(\u0026#34;check error\\r\\n\u0026#34;); data_send(ptrespconn, false, NULL); return ; } if(save_data(pusrdata, length) == false){ os_printf(\u0026#34;save_data error\\r\\n\u0026#34;); data_send(ptrespconn, false, NULL); return ; } pURL_Frame = (URL_Frame *)os_zalloc(sizeof(URL_Frame)); parse_url(precvbuffer, pURL_Frame); os_printf(\u0026#34;Type[%d]\\r\\n\u0026#34;, pURL_Frame-\u0026gt;Type); os_printf(\u0026#34;pSelect[%s]\\r\\n\u0026#34;, pURL_Frame-\u0026gt;pSelect); os_printf(\u0026#34;pCommand[%s]\\r\\n\u0026#34;, pURL_Frame-\u0026gt;pCommand); os_printf(\u0026#34;pFilename[%s]\\r\\n\u0026#34;, pURL_Frame-\u0026gt;pFilename); switch (pURL_Frame-\u0026gt;Type) { case GET: os_printf(\u0026#34;We have a GET request.\\n\u0026#34;); if((strlen(pURL_Frame-\u0026gt;pSelect) == 1) \u0026amp;\u0026amp; (os_strncmp(pURL_Frame-\u0026gt;pSelect, \u0026#34;/\u0026#34;, 1) == 0)){ //获取webconfig页面 html_send(ptrespconn, true, HTML); } if(os_strcmp(pURL_Frame-\u0026gt;pSelect, \u0026#34;config\u0026#34;) == 0 \u0026amp;\u0026amp; os_strcmp(pURL_Frame-\u0026gt;pCommand, \u0026#34;command\u0026#34;) == 0) { os_memset(szOut, 0, sizeof(szOut)); #if PLUG_DEVICE stat = user_plug_getmethed(szOut); #elif FAN_DEVICE stat = user_fan_getmethed(szOut); #elif DS18B20_DEVICE stat = user_ds18b20_getmethed(szOut); #endif if(stat==0){ os_printf(\u0026#34;get22szOut[%s]\\r\\n\u0026#34;, szOut); data_send(ptrespconn, true, szOut); }else data_send(ptrespconn, false, NULL); }else data_send(ptrespconn, false, NULL); break ; case POST: os_printf(\u0026#34;We have a POST request.\\n\u0026#34;); pParseBuffer = (char *)os_strstr(precvbuffer, \u0026#34;\\r\\n\\r\\n\u0026#34;); if (pParseBuffer == NULL) { break; } pParseBuffer += 4; os_printf(\u0026#34;pParseBuffer[%s]\\r\\n\u0026#34;, pParseBuffer); if((strlen(pURL_Frame-\u0026gt;pSelect) == 1) \u0026amp;\u0026amp; (os_strncmp(pURL_Frame-\u0026gt;pSelect, \u0026#34;/\u0026#34;, 1) == 0)){ //webconfig配网 webconfig_set_wifi(pParseBuffer); data_send(ptrespconn, false, NULL); break ; }else if (os_strcmp(pURL_Frame-\u0026gt;pSelect, \u0026#34;config\u0026#34;) == 0 \u0026amp;\u0026amp; os_strcmp(pURL_Frame-\u0026gt;pCommand, \u0026#34;command\u0026#34;) == 0) { os_memset(szOut, 0, sizeof(szOut)); #if PLUG_DEVICE stat = user_plug_postmethed(pParseBuffer, szOut); #elif FAN_DEVICE stat = user_fan_postmethed(pParseBuffer, szOut); #elif DS18B20_DEVICE stat = user_ds18b20_postmethed(pParseBuffer, szOut); #endif if(stat == 0){ os_printf(\u0026#34;post22szOut[%s]\\r\\n\u0026#34;, szOut); data_send(ptrespconn, true, szOut); }else data_send(ptrespconn, false, NULL); } else { os_printf(\u0026#34;\\r\\nparse error!\\r\\n\u0026#34;); data_send(ptrespconn, false, NULL); } break ; } if (precvbuffer != NULL){ os_free(precvbuffer); precvbuffer = NULL; } os_free(pURL_Frame); pURL_Frame = NULL; } 最后 很简单。本人参考使用Web页面配置ESP8266的参数思路开发修改。\ngit链接https://gitee.com/review/esp-01.git\n","date":"2018-05-14T22:52:58+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:52:58+08:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E4%B9%8Besp-01webconfig%E9%85%8D%E7%BD%91/","title":"树莓派之esp-01webconfig配网"},{"content":"前言 很久没更新了，继续esp智能开关。本文讲述esp-01智能开关的硬件和原理图（PS:本人不是电子专业，只是开了头不得不去搞，如有误请多包含）\n硬件材料 设备 价格 esp8266-01 11元 220V转3.3v模块 5元 5V继电器（低电平触发） 8元 洞洞板 1元 电路图 电路中不包含烧写电路。 ###简述 电路如图，需要注意的是继电器使用低电平触发。高电触发easp-01上电后无法启动，我怀疑是gpio2接入继电器上电被拉低导致无法启动。改成低电平触发就没有问题。\n效果图 参考 手把手使用esp8266一起来做智能插排-硬件篇 ","date":"2018-04-03T22:41:29+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:41:29+08:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E4%B9%8Besp-01%E6%99%BA%E8%83%BD%E5%BC%80%E5%85%B3%E7%A1%AC%E4%BB%B6/","title":"树莓派之esp-01智能开关硬件"},{"content":"前言 家里装修已经有段时间了，当时使用的6类线千兆网线。现在有点时间做个内网测速。\n测试准备 设备 - 路由器 斐迅K3（openWRT固件） 网线 山泽(SAMZHE)SZ-6050 6类非屏蔽线 电脑 WIN7 64位 硬盘 西数1T蓝盘 测试工具 iperf3 测试 服务端（路由器） 路由器上安装iperf3工具 opkg install iperf3 开启测试服务 iperf3 -s -f M 客户端（PC） 安装iperf3工具 测试命令 iperf3.exe -c 192.168.1.1 -b 1000M -t 60 -i 10 -f M 测试结果 服务端 客户端 结果分析 查了下网络评论千兆网能跑100M左右。我这才70+不知道是不是，路由器（客厅）至PC（卧室）线路长所导致。\n","date":"2018-02-05T22:37:26+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%AE%B6%E5%BA%AD%E5%8D%83%E5%85%86%E7%BD%91%E7%BA%BF%E5%86%85%E7%BD%91%E6%B5%8B%E9%80%9F/","title":"家庭千兆网线内网测速"},{"content":"开发原由： 工作之余使用esp8266自己DIY做智能开关，采用Homeassistant RESTful Switch组件。硬件、固件搞定后安装到DIY的空气净化器上还是很不错的。后面想想好像可以使用esp8266的PWM调速。但问题来了，RESTful Switch组件不带没有调节参数。犹豫是换mqtt还是自己开发RESTful Light，看了半于mqtt协议一头的晕，然后选择后者。只是简单的适配，现和大家一起分享一下\n效果\n安装： 下载解压文件，复制到.homeassistant\\custom_components\\light 配置： light: - platform: rest resource: [url]http://192.168.1.243/config?command=light[/url] body_on: \u0026#39;{\u0026#34;Response\u0026#34;:{\u0026#34;brightness\u0026#34;:{{ brightness }}, \u0026#34;status\u0026#34;:\u0026#34;true\u0026#34;}}\u0026#39; #brightness是homeassistant的亮度参数 body_off: \u0026#39;{\u0026#34;Response\u0026#34;:{\u0026#34;brightness\u0026#34;:{{ brightness }}, \u0026#34;status\u0026#34;:\u0026#34;false\u0026#34;}}\u0026#39; is_on_template: \u0026#39;{{ value_json[\u0026#34;Response\u0026#34;][\u0026#34;status\u0026#34;] }}\u0026#39; 附件:https://image.vitshare.cn/blog/content/post/基于RESTful_Switch适配RESTful_Light可亮度/rest.rar\n后续工作： 1、esp8266-01硬件设计（PWM信号放大电路） 2、esp8266-01固件开发（主要工作PWM调节负载电压） 3、完成RESTful Light支持RGB调节\n参考： Jinjin2 通用模板语言\nHomeassistant light组件源码\n2b-esp8266_sdk_iot_demo_cn_v1.3.pdf\n","date":"2017-12-22T00:00:00Z","image":"https://picsum.photos/800/600?r=2017-12-22T22:37:26+08:00","permalink":"https://zfj1441.com/p/%E5%9F%BA%E4%BA%8Erestful-switch%E9%80%82%E9%85%8D%E4%BA%86restful-light%E5%8F%AF%E4%BA%AE%E5%BA%A6/","title":"基于RESTful Switch适配了RESTful Light可亮度"},{"content":"前言 简单自我介绍下。 自学python，半桶水。业余时间，写写爬虫、搞搞插件开发。 本月接触homeassistant 自己的K3路由器用的LEDE 找了半天没找到device_tracker追踪插件。索性看看homeassistant源码，发现luci.py这货。看了一遍后基本了解了思路，说白了就是个爬虫，爬路由器而已。\n正文 上周末研究 LEDE登录方式，改luci.py程序。测试这么多天来没问题。但是退出时间像斐讯K3官Root【更新】【设备追踪】【device tracker】插件说的有点长。3分钟。 今天早早下班就继续在源码里翻滚。\n先是找到了这个\n再找到了这个\n最后找到了这个，就恍然大悟了。\n最终解决离开时间长问题。\n安装方法： 下载解压文件，复制到.homeassistant\\custom_components\\device_tracker\n配置yaml:\ndevice_tracker: - platform: lede host: 192.168.1.1 username: root password: 你的密码 track_new_devices: no consider_home: 30 #检查离开时间，默认180秒（3分钟） 附件：https://image.vitshare.cn/blog/content/post/homeassistant斐讯LEDE固件设备追踪插件/lede.rar\n最后申明： 插件有点粗糙请见谅。有能力改改，没能回帖有空我看看。\n总结 无\n","date":"2017-11-30T00:00:00Z","image":"https://picsum.photos/800/600?r=2017-11-30T22:37:26+08:00","permalink":"https://zfj1441.com/p/homeassistant%E6%96%90%E8%AE%AFlede%E5%9B%BA%E4%BB%B6%E8%AE%BE%E5%A4%87%E8%BF%BD%E8%B8%AA%E6%8F%92%E4%BB%B6/","title":"homeassistant斐讯LEDE固件设备追踪插件"},{"content":"前言 记录下工作中的问题及解决方法\n正文 python中mysql查询返回字典 查询mysql数据，表中有数据，select条件无异常就是查不出数据。 生成流水序号 字符编码“UnicodeDecodeError: \u0026lsquo;utf8\u0026rsquo; codec can\u0026rsquo;t decode byte 0xce in position 149: invalid continuation byte” 问题一： 在连接数据库时设置cursorclass类如下：\nself.conn = MySQLdb.connect(host=gl.DBSEVER['IP'], port=gl.DBSEVER['PORT'], user=gl.DBSEVER['USER'], passwd=gl.DBSEVER['PASSWORD'], db=gl.DBSEVER['DBNAME'], charset=\u0026quot;utf8\u0026quot;, cursorclass=MySQLdb.cursors.DictCursor ) 问题二： 在5.1.73版本和5.6.26版本的mysql中查询语句没有问题；但在5.7.19版本的mysql中一直查不到。使用数据库工具执行命令可查。 最终确实是事务问题 在执行select语句后提交事务。\nsql = \u0026quot;select * from test\u0026quot;; cursor = conn.cursor() cursor.execute(sql) conn.commit() 问题三： 网上好多使用mysql存储过程实现，我自己就使用python代码试了下。10个线程同时取流水号没有重复。效率没测试。\ndef getSeqno(self): '''取流水号''' try: cur = self.conn.cursor() cur.execute(\u0026quot;update seqlist set id = id + 1 where NAME='SEQNO'\u0026quot;) cur.execute('select id from seqlist') self.conn.commit() row = cur.fetchone() cur.close() except Exception, e: gl.logmain.error(e) raise seq = row['id'] return seq 问题四： python字符编码与mysql字符编码不符合。\n数据库设置成utf8编码\nvi/etc/my.cnf 在里面加入，已经有[XXX]的，在里面直接加入即可。 [mysqld] character-set-server=utf8 [client] default-character-set=utf8 [mysql] default-character-set=utf8 python接连数据库设置utf8编码\nself.conn = MySQLdb.connect(host=gl.DBSEVER['IP'], port=gl.DBSEVER['PORT'], user=gl.DBSEVER['USER'], passwd=gl.DBSEVER['PASSWORD'], db=gl.DBSEVER['DBNAME'], charset=\u0026quot;utf8\u0026quot;, cursorclass=MySQLdb.cursors.DictCursor) 将数据语句转成utf8编码格式\nimport sys reload(sys) sys.setdefaultencoding('utf-8') sql = \u0026quot;UPDATE tset set a=%s\u0026quot;, \u0026quot;你好“) sql = sql.encode('utf-8') 其它记录： mysql插入字典\nrow = dict(row) columns = ', '.join(row.keys()) placeholders = ', '.join(['%s'] * len(row)) sql = \u0026quot;INSERT into test ( %s ) VALUES ( %s )\u0026quot; % (columns, placeholders) sql = sql.encode('utf-8') mysql更新字典\nrow = dict(row) columns = '=%s, '.join(row.keys()) columns = columns + '=%s' sql = \u0026quot;UPDATE test set %s where idx='%s' \u0026quot; % (columns, \u0026quot;100\u0026quot;) sql = sql.encode('utf-8') cur = self.conn.cursor() cur.execute(sql, row.values()) 总结 工作中问题还是很多，这只是一小部分。记录下以备用\n","date":"2017-09-05T22:34:53+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:34:53+08:00","permalink":"https://zfj1441.com/p/python%E4%BD%BF%E7%94%A8mysql%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/","title":"Python使用mysql问题汇总"},{"content":"前言 继上篇树莓派养成之路 ——siri 控制LED灯后一直想着怎么将线路接入真实生活中。由于测试都是使用导线将灯或继电器和树莓派连接，如果家里有多个设备就会出现如下问题：\nGPIO口数量不够 实体线部线（PS：在树莓派养成之路 ——siri 控制LED灯中至少要将GPIO口和灯或继电器连接），设备一多，线路满天飞。 基于上述两个问题，自然而然的就想到使用wifi控制设备。随后看到esp-01经济实惠的wifi模块。后续几篇我将从电路、程序、homebridge通信、siri控制等方面介绍下我这引些天使用esp-01无线模块自己打造**\u0026ldquo;智能开关\u0026rdquo;**\n正文 下面简单描述下打造“智能开关”的步骤\n整体框架设计和模块功能确定 整体框架 从连接方式上看；手机、树莓派和wifi模块通过路由器连接并通信（屁话）。wifi模块提供服务接收指令控制GPIO口控制达到控制灯开关的目的。\n模块功能 “智能开关”设计之初是为了控制灯。可以手动控制也可以通过软件控制。\nesp-01模块了解 esp-01是封装esp8266芯片的wifi模块。\nwifi模块 可烧固件 有3个GPIO口 有详细的开发手册、SDK文档、API文档 详细的DEMO 了解homebridge和esp-01通信 了解homebridge和esp-01通信主要是为了方便编写固件和homebridge插件。这里主要参考homebridge_yeelight插件。\n通信报文采用json格式 {\u0026quot;id\u0026quot;:-1,\u0026quot;method\u0026quot;:\u0026quot;get_prop\u0026quot;,\u0026quot;power\u0026quot;:\u0026quot;on\u0026quot;} 通信流程： homebridge_yeelight会启动UDP服务。智能开关通电会启动tcp服务端和UDP客户端，通过UDP客户端发送智能开关的信息（主要是设备ID、IP、端口）,homebridge_yeelight收到UDP信息通过tcp客户端连接上智能开关并保持通信。\nesp-01固件开发和homebridge插件开发 esp-01固件开发 开发环境搭建。交叉编译工具、固件烧写工具 设备工具、烧写电路、测试电路 demo编译测试 程序开发编译烧写 homebridge插件开发 这里主要是修改homebridge_yeelight插件\n组装测试及修改 这步主要是迭代上面两步，直到项目结束。\n结束 这是本人总结的基本的开发步骤，后续会陆续更新开发流程。最后送上一段测试视屏\n","date":"2017-08-04T22:06:59+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T22:06:59+08:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E4%B9%8Besp-01%E6%99%BA%E8%83%BD%E5%BC%80%E5%85%B3/","title":"树莓派之esp-01智能开关"},{"content":"前言 从去年下半年开始就在折腾路由器刷固件、内网穿透、ss代理、脚本编写,这认我对路由器有了新的了解，说实话没折腾之前就只觉得路由器只能拨号。最近K3已经刷好固件正式上岗，随之淘汰下K1s。本人工作环境没有提供外网，但覆盖chinanet，这就琢磨着使用k1s（已刷[老毛子Padavan固件][2]）做个桥接并拨号共享。\n网络链接原理图 简述： 首先使用2.4G的wifi桥接chinanet至wan口；然后wan口使用ppoe拨号；最后5G网络共享。\n实现步骤 按住简述所说的，实现有3步：\n2.4G网络桥接[参考][1]\n基本都是按教程做，这里我就上传几张我设置的图给大家参考下。 wan口ppoe拨号\n这步值得提的是某淘上买的账号基本上都是外省的。使用ppoe拨号起初一直认证不过，让我灰心好几个小时。后面看到http://blog.sina.com.cn/s/blog_67493dad01015g2l.html虽然帖子有点久远，但其中这段话很管用，最终帮我拨号成功\n电脑端和手机端方法一样：电脑连wifi登录界面无限循环解决方法\u0026mdash;\u0026ndash;用户名：（账号@cw.开户地首字母.chntel.com）或者（账号@wlan.开户地首字母.chntel.com）例如，我们给出的账号是：13398858666 密码：123456 开户地：云南 你们进入chinanet界面变成，再在其他账号栏中输入，用户名：13398858666@cw.YN.chntel.com 密码：123456 开户地选择：云南 最后连接即可。（YN是“云南”的首字母缩写，如果是浙江的号那么就把YN改成ZJ）\n5G网络共享\n完成上面两步基本上已经大功告成，这步就没什么说的，基本的设置就可以了。 使用结果 由于使用2.4G桥接，5G网络共享身边同事部分设置不支持5G网络，所以建议在没有网速要求的情况下使用5G桥接,2.4G网络共享。\n参考 恩山论坛\n","date":"2017-05-27T21:52:22+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T21:52:22+08:00","permalink":"https://zfj1441.com/p/%E6%96%90%E8%AE%AFk1s%E8%B7%AF%E7%94%B1%E5%99%A8%E6%A1%A5%E6%8E%A5chinanet%E6%8B%A8%E5%8F%B7%E5%85%B1%E4%BA%AB/","title":"斐讯K1s路由器桥接chinanet拨号共享"},{"content":"前言 上篇文章做了个小测试程序python对树莓派GPIO引脚控制实现了流水灯，本篇继续深入实现微信远程控制LED流水灯\n正文 先看下整体的框架\n简单说明\n这里借用两个微信号，互为好友。其中微信1做为服务端部署在树莓派上；微信2做为客户端用户通过微信2发出指令，实现远程控制。\n技术要点\n树莓派调用LED灯上篇已经实现树莓派养成之路 ——GPIO控制 服务端微信1使用itchat1. 实现方法 1 、 树莓派上安装itchat库\npip install itchat 2 、 编写服务端程序，得到命令后调用led.py控制LED灯\nitchatBot.py\nimport itchat import led #itchat框架，关注TEXT消息 @itchat.msg_register(itchat.content.TEXT) def text_reply(msg): #得到任何text消息就打开流水灯，最后原消息返回 led.openLed() print(msg.text) return msg.text itchat.auto_login(enableCmdQR=2) itchat.run() led.py\nimport RPi.GPIO as GPIO import time def setup(): GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.setup(13, GPIO.OUT) GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) def destroy(): GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) GPIO.setup(11, GPIO.IN) GPIO.setup(13, GPIO.IN) def openLed(): setup() GPIO.output(13, GPIO.HIGH) for i in range(2): GPIO.output(11,GPIO.HIGH) time.sleep(1) GPIO.output(11, GPIO.LOW) time.sleep(1) # destroy() GPIO.cleanup() if __name__==\u0026#34;__main__\u0026#34;: openLed() 后记 原计划是想部个服务，然后手机调服务的方式实现远程控。但想想这样是不是很不安全。使用微信做为渠道局限性大，但安全得到保障。下篇《siri远程GPIO控制》\nitchat littlecodersh大牛对网页微信封装的一套API\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2017-05-17T21:45:53+08:00","image":"https://picsum.photos/800/600?r=2024-05-06T21:45:53+08:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E4%B9%8B%E5%BE%AE%E4%BF%A1%E8%BF%9C%E7%A8%8Bgpio%E6%8E%A7%E5%88%B6/","title":"树莓派之微信远程GPIO控制"},{"content":"前言 对本月实现微信远程控制家里电路的计划，补步学习树莓派python GPIO控制。其中查看了网上的些资料还发现了点部问题。\n正文 看了网上多数都是正极直接级VCC，然后控制物理接口11的输出。但问题在于初始化11接口后LED灯就常亮，所以我直接使用11，13两个口。看下我的接法： 我大部分参考树莓派开发系列教程9——树莓派GPIO控制 帮助了我很多，但也给我埋了个坑。 程序初始化11接口为输出模式，程序退出后没有复位。导致程序下次执行警告，最终问题是程序退出后LED保持最后的状态。警告信息如下：\n其实我也对GPIO也不是太懂，最后看到文章树莓派学习笔记——RPi.GPIO 流水灯中的代码，退出程序前将接口设置为低电平输入模式。\n最后看看我的程序\n#!/usr/bin/env python import RPi.GPIO as GPIO import time def setup(): \u0026#39;\u0026#39;\u0026#39;初始化接口\u0026#39;\u0026#39;\u0026#39; GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) GPIO.setup(13, GPIO.OUT) GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) def destroy(): \u0026#39;\u0026#39;\u0026#39;恢复接口\u0026#39;\u0026#39;\u0026#39; GPIO.output(11, GPIO.LOW) GPIO.output(13, GPIO.LOW) GPIO.setup(11, GPIO.IN) GPIO.setup(13, GPIO.IN) setup() GPIO.output(13, GPIO.HIGH) for i in range(10): GPIO.output(11,GPIO.HIGH) time.sleep(1) GPIO.output(11, GPIO.LOW) time.sleep(1) destroy() 20170517 树莓派GPIO入门01-使用GPIO接口控制发光二极管闪烁 的源码中GPIO.cleanup()对所有GPIO引脚做清理\n再看看效果\n最后零时找了个在线画电路图，平时不常用。\n参考： 树莓派开发系列教程9——树莓派GPIO控制 树莓派学习笔记——RPi.GPIO 流水灯 树莓派的pythonGPIO编程整理\n","date":"2017-05-10T07:44:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-06T13:14:18-04:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E6%8E%A7%E5%88%B6gpio%E7%82%B9%E4%BA%AEled%E7%81%AF/","title":"树莓派控制GPIO点亮LED灯"},{"content":"前言 天天在linux/unix下开发，闲暇之娱也玩玩硬件。想着有时间用树莓派做个智能家居做个听使唤的管家(先 YY下)。\n正文 硬件\n材料 数量 树莓派3 1 外壳+风扇+散热片 1 8G U盘 1 sd卡 1 软件\n软件名 说明 win32diskimager-v0.9-binary 写镜像 Xshell 远程终端 VNC-Viewer-5.0.3-Windows-32bit 远程桌面 工具下载地址：我是地址 密码：zj0c\n方法 官方下载镜像https://www.raspberrypi.org/downloads/，Raspbian系统是树莓派官方推荐的系统。 将解压出img镜像文件，写入U盘。 将U盘中的boot分区内的所有文件复制到sd中(大约20+M),修改cmdline.txt中; root=/dev/mmcblk0p2 修改为 root=/dev/sda2 最新的2017-04-10-raspbian-jessie.img中没有root=/dev/mmcblk0p2,所以我选择了2017-01-11-raspbian-jessie.img\n4. 在SD卡中新建ssh文件，目的是认系统启动后自动开启ssh服务 5. 安装VNC桌面服务\n使用Xshell登入后台。Raspbian默认的用户名密码是pi/raspberry 安装VNC服务 sudo apt-get install tightvncserver 增加一个桌面 tightvncserver 启动服务vncserver（我设置密码后默认启动） vncserver 使用VNC-Viewer-5.0.3-Windows-32bit 登陆 最后是最张的图片\n20250612更新 最近发现homeassistang系统历史数据无法读取，重启几次后依旧无法解决。登录后台排出后发现数据库数据无法写入，最后定位sd卡读写异常。今天新买了张sd卡重新安装系统\n正文 1.准备一个8G 以上的SD卡 2.安装系统烧录工具Raspberry Pi Imager(https://www.raspberrypi.com/software/) 3.烧写系统 4.插上SD卡，上电启动 5.修改root密码 sudo passwd 6.扩展SD卡剩余空间 raspi-config Advanced Options-\u0026gt;Expand Filesystem 7.更新系统 sudo apt update sudo apt upgrade -y 8.安装docker # 安装Docker的依赖 sudo apt install apt-transport-https ca-certificates curl software-properties-common -y #添加Docker的官方GPG密钥 curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - #设置Docker仓库 sudo add-apt-repository \u0026#34;deb [arch=armhf] https://download.docker.com/linux/raspbian/ $(lsb_release -cs) stable\u0026#34; sudo apt update #安装Docker CE（社区版） sudo apt install docker-ce -y #添加国内源 vi /etc/docker/damen.json { \u0026#34;registry-mirrors\u0026#34;: [ \u0026#34;https://docker.m.daocloud.io\u0026#34;, \u0026#34;https://noohub.ru\u0026#34;, \u0026#34;https://huecker.io\u0026#34;, \u0026#34;https://dockerhub.timeweb.cloud\u0026#34;, \u0026#34;https://0c105db5188026850f80c001def654a0.mirror.swr.myhuaweicloud.com\u0026#34;, \u0026#34;https://5tqw56kt.mirror.aliyuncs.com\u0026#34;, \u0026#34;https://docker.1panel.live\u0026#34;, \u0026#34;http://mirrors.ustc.edu.cn/\u0026#34;, \u0026#34;http://mirror.azure.cn/\u0026#34;, \u0026#34;https://hub.rat.dev/\u0026#34;, \u0026#34;https://docker.ckyl.me/\u0026#34;, \u0026#34;https://docker.chenby.cn\u0026#34;, \u0026#34;https://docker.hpcloud.cloud\u0026#34;, \u0026#34;https://docker.m.daocloud.io\u0026#34; ] } #重启docker服务 systemctl restart docker.socket systemctl restart docker.service 9.postgres数据库安装 mkdir -p /root/postgresql/data docker run -d \\ -v /root/postgresql/data:/var/lib/postgresql/data \\ -e POSTGRES_PASSWORD=postgres \\ -p 127.0.0.1:5432:5432 \\ --name postgres \\ postgres 10.安装mqtt服务器 mkdir -p /root/mosquitto/config mkdir -p /root/mosquitto/data mkdir -p /root/mosquitto/log 11.扩展虚拟swap https://blog.csdn.net/dsz1714/article/details/140845834 参考内容 树莓派系统安装在u盘，利用优盘启动 树莓派入门之装系统 ","date":"2017-05-08T07:44:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T07:44:18-04:00","permalink":"https://zfj1441.com/p/%E6%A0%91%E8%8E%93%E6%B4%BE%E5%AE%89%E8%A3%85raspbian%E7%B3%BB%E7%BB%9F%E6%95%99%E7%A8%8B/","title":"树莓派安装Raspbian系统教程"},{"content":"前言 之前一直没太认真琢磨。觉得网上有，不会的百度下。就是抱着这种态度，简单的几个技巧现在才一知半解。\n正题 shell脚本字符串截取我大概想到有如下几种方式：\nsed awk cut 通过# % 截取（不知道应该叫什么方法） 本文详细说明下第4种方式。为什么不说前三种呢，我个人觉得前三个命令用来截取字符串有点高射炮打蚊子。\n# 号截取，删除左边字符，保留右边字符，匹配第一个 eg: var=http://www.vr7jj.com/api str=${var#*/} 最后str为 /www.vr7jj.com/api\n## 号截取，删除左边字符，保留右边字符，匹配最后一个 eg: var=http://www.vr7jj.com/api str=${var##*/} 最后str为 api\n% 号截取，删除右边字符，保留左边，匹配第一个 eg: var=http://www.vr7jj.com/api str=${var%/*} 最后str为 http://www.vr7jj.com\n%% 号截取，删除右边字符，保留左边，匹配最后一个 eg: var=http://www.vr7jj.com/api str=${var%%/*} 最后str为 http:\n扩展 按位截取 eg: var=http://www.vr7jj.com/api str1=${var:0:4} #左边第0个字符开始截3个数 结果 http str2=${var:7} #左边第7个字符一直到结束 结果 www.vr7jj.com/api str3=${var:0-15:10}#右边第15个字符开始取10位 str4=${var:0-6}#右边第几个字符开始一直到结束 总结 记忆方法 # 号脚本中用于注释常在行开头左边。所以#是从左边开发截取; %反这从右边截取。（用的少了，脑子不好使，常忘记）\n","date":"2017-04-27T07:44:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-06T13:02:18-04:00","permalink":"https://zfj1441.com/p/shell%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%88%AA%E5%8F%96/","title":"shell字符串截取"},{"content":"前言 开发机器IP固定，路由上有限制，不通直连。是个很郁闷的事。有个时间在网上看到的ssh代理转发。命令功能很强大。在这我就对自己了解的画了个间图方便大家理解。\n正向代理 “本机”连上“ssh服务器”，然后把”ssh服务器”能访问的机器地址和端口（当然也包括“ssh服务器”自己）镜像到”本机”的端口上。\n命令：\nssh -L [客户端IP或省略]:[客户端端口]:[服务器侧能访问的IP]:[服务器侧能访问的IP的端口] [登陆服务器的用户名@服务器IP] -p [服务器ssh服务端口（默认22）]\n其中，客户端IP可以省略，省略的话就是127.0.0.1了，也就是说只能在客户端本地访问。服务器IP都可以用域名来代替。\n举例说明：\n本地IP192.168.1.2，你可以ssh到某台服务器8.8.8.8，8.8.8.8可以访问8.8.4.4，你内网里还有一台机器（192.168.1.88）可以访问你。\n如果你想让内网里的另外一台电脑访问8.8.4.4的80端口的http服务，那么可以执行：\nssh -CfNg -L 192.168.1.2:8080:8.8.4.4:80 test@8.8.8.8\nC表示压缩数据传输 f表示后台用户验证,这个选项很有用,没有shell的不可登陆账号也能使用. N表示不执行脚本或命令 g表示允许远程主机连接转发端口\n也就是说，ssh到8.8.8.8上，然后让8.8.8.8把8.8.4.4的80端口映射到本地的8080端口上，而且和本地192.168.1.2这个IP绑定。\n内网里的另外一台机器可以通过IE浏览器中输入http://192.168.1.2:8080查看8.8.4.4的网页\n反向代理 本机”连上“ssh服务器”，然后把“本机”能访问的机器地址和端口（也包括”本机”自己）镜像到“ssh服务器”的端口上。\n反向连接用得可能更多一些。比如你的客户端在内网，在外网是无法直接访问到的，这时用反向连接打通一条隧道，就可以从外网通过这条隧道进来了。\n命令：\nssh -R [服务器IP或省略]:[服务器端口]:[客户端侧能访问的IP]:[客户端侧能访问的IP的端口] [登陆服务器的用户名@服务器IP] -p [服务器ssh服务端口（默认22）]\n其中，服务器IP如果省略，则默认为127.0.0.1，只有服务器自身可以访问。指定服务器外网IP的话，任何人都可以通过[服务器IP:端口]来访问服务。当然，这个时候服务器本机也要输入外网IP:端口来访问。\n举例说明：\n“本机”IP 192.168.1.2，你可以ssh到外网某台服务器8.8.8.8，你内网里有一台机器192.168.1.3。\n如果你想让外网所有的能访问8.8.8.8的IP都能访问192.168.1.3的http服务，那么可以执行：\nssh -CfNg -R 8.8.8.8:8080:192.168.1.3:80 test@8.8.8.8\nC表示压缩数据传输 f表示后台用户验证,这个选项很有用,没有shell的不可登陆账号也能使用. N表示不执行脚本或命令 g表示允许远程主机连接转发端口\n也就是说，ssh到8.8.8.8上，然后把本地局域网内192.168.1.3的80端口映射到8.8.8.8的8080端口上，这样外网任何一台可以访问8.8.8.8的机器都可以通过8080端口访问到内网192.168.1.3机器的80端口了。\n参考 http://blog.csdn.net/linsanhua/article/details/17360369 http://www.cnblogs.com/wangkangluo1/archive/2011/06/29/2093727.html\n","date":"2017-01-05T23:44:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-06T12:46:18-04:00","permalink":"https://zfj1441.com/p/%E4%BD%BF%E7%94%A8ssh%E4%BB%A3%E7%90%86%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E4%BB%A3%E7%90%86/","title":"使用ssh代理快速搭建代理"},{"content":"起因 获取网页我是这样做的。\nr = requests.get(url,verify=False) print r.text.encode(\u0026#39;utf-8\u0026#39;) 处理 打印处理的内容: 用浏览器去访问网页，查看编码方式确实是utf-8\n随后去百度一些解决问题： 结论 有问题多百度，多查资料\n参考 segmentfault\n","date":"2017-01-05T13:39:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-06T12:44:18-04:00","permalink":"https://zfj1441.com/p/python%E7%88%AC%E8%99%ABrequests%E8%8E%B7%E5%8F%96%E7%BD%91%E9%A1%B5%E4%B9%B1%E7%A0%81/","title":"python爬虫requests获取网页乱码"},{"content":"前言 最近一个项目对接。要使用SHA1WithRSA签名验签。 之前接触过DES、3DES、 AES、 SHA1、 MD5、RSA 看这加密想当然的觉得是就是先对数据做个SHA1摘要再做个RSA加密嘛，简单不是。 man 了一下 openssl 关于RSA加解密。霹雳啪啦几个小时就把 理解的“SHA1WithRSA”实现了。 但是测试时对方就是验签不过。 后面还是问广大的网络。才让我得了解自己的无知。\n简单阅读一下就能大概知道实际上可以通过openssl的RSA_sign实现 来看一下RSA_sign\n#include \u0026lt;openssl/rsa.h\u0026gt; int RSA_sign(int type, const unsigned char *m, unsigned int m_len, unsigned char *sigret, unsigned int *siglen, RSA *rsa); int RSA_verify(int type, const unsigned char *m, unsigned int m_len, unsigned char *sigbuf, unsigned int siglen, RSA *rsa); DESCRIPTION RSA_sign() signs the message digest m of size m_len using the private key rsa as specified in PKCS #1 v2.0. It stores the signature in sigret and the signature size in siglen. sigret must point to RSA_size(rsa) bytes of memory. Note that PKCS #1 adds meta-data, placing limits on the size of the key that can be used. See RSA_private_encrypt for lower-level operations. type denotes the message digest algorithm that was used to generate m. If type is NID_md5_sha1, an SSL signature (MD5 and SHA1 message digests with PKCS #1 padding and no algorithm identifier) is created. RSA_verify() verifies that the signature sigbuf of size siglen matches a given message digest m of size m_len. type denotes the message digest algorithm that was used to generate the signature. rsais the signer’s public key. RETURN VALUES RSA_sign() returns 1 on success. RSA_verify() returns 1 on successful verification.\n简单看看，其实RSA_sing()函数第一为送NID_sha1\n通过这篇文章知道需要签名的是SHA1摘要而非所有报文。所以要先对明文数据做SHA1。再调用 RSA_sign()签名。\n结论： 对openSSL源码一知半解的我，竟把RSA加密|解密和RSA签名|验证混淆。 最后 私钥加密 ≠ 签名\n20170519更新 这几天看了下这文章阅读人数相比还挺多的。空闲之余就把整理下自己以前写的test程序提供给大家参考下。\n秘钥对是我是使用openssl命令生产的给出的参考如下。另外秘钥文件格式有很多种，请注意。\n生产私钥 openssl genrsa -out userkey.pem 1024 从私钥中导出公钥 openssl rsa in userkey.pem -pubout -out userpub.key 签名测试代码\n#include \u0026lt;string.h\u0026gt; #include \u0026lt;openssl/rsa.h\u0026gt; #include \u0026lt;openssl/pem.h\u0026gt; #include \u0026lt;openssl/err.h\u0026gt; RSA* getPrivateKey(char* in_szKeyPath) { FILE *fp = NULL; char szKeyPath[1024]; RSA *priRsa = NULL, *pubRsa = NULL, *pOut = NULL; memset(szKeyPath, 0 ,sizeof(szKeyPath)); if(256 \u0026lt; strlen(in_szKeyPath)) strncpy(szKeyPath, in_szKeyPath, 256); else strncpy(szKeyPath, in_szKeyPath, strlen(in_szKeyPath)); printf(\u0026#34;密钥文件路径[%s]\u0026#34;, szKeyPath); /* 打开密钥文件 */ if(NULL == (fp = fopen(szKeyPath, \u0026#34;rb\u0026#34;))) { printf( \u0026#34;打开密钥文件[%s]出错\u0026#34;, szKeyPath); return NULL; } /* 获取私密钥 */ if(NULL == (priRsa = PEM_read_RSAPrivateKey(fp, \u0026amp;priRsa, NULL,NULL))) { printf( \u0026#34;读出私钥内容出错\\n\u0026#34;); fclose(fp); return NULL; } fclose(fp); printf(\u0026#34;提取私钥\\n\u0026#34;); pOut = priRsa; return pOut; } RSA* getPublicKey(char* in_szKeyPath) { FILE *fp = NULL; char szKeyPath[1024]; RSA *priRsa = NULL, *pubRsa = NULL, *pOut = NULL; memset(szKeyPath, 0 ,sizeof(szKeyPath)); if(256 \u0026lt; strlen(in_szKeyPath)) strncpy(szKeyPath, in_szKeyPath, 256); else strncpy(szKeyPath, in_szKeyPath, strlen(in_szKeyPath)); printf(\u0026#34;密钥文件路径[%s]\u0026#34;, szKeyPath); /* 打开密钥文件 */ if(NULL == (fp = fopen(szKeyPath, \u0026#34;rb\u0026#34;))) { printf( \u0026#34;打开密钥文件[%s]出错\u0026#34;, szKeyPath); return NULL; } /* 获取公密钥 */ if(NULL == (priRsa = PEM_read_RSA_PUBKEY(fp, \u0026amp;priRsa, NULL,NULL))) { printf(\u0026#34;读出私钥内容出错\\n\u0026#34;); fclose(fp); return NULL; } fclose(fp); printf(\u0026#34;提取公钥\\n\u0026#34;); pOut = priRsa; return pOut; } int main(void) { int flen,rsa_len, ienLen, iRet; RSA *prsa = NULL; char szEnData[]=\u0026#34;orderId=01010500201502000004reqTime=20150205012727ext=20151120ext2=1\u0026#34;; char szTmp[10240], szTmp1[10240]; if(NULL == (prsa = getPrivateKey(\u0026#34;userkey.pem\u0026#34;))) { RSA_free(prsa); printf(\u0026#34;获取私钥失败\\n\u0026#34;); return -1; } // RSA_print_fp(stdout, prsa, 11); flen = strlen(szEnData); printf(\u0026#34;待签名数据:[%s]\\n\u0026#34;, szEnData); memset(szTmp, 0, sizeof(szTmp)); memset(szTmp1, 0, sizeof(szTmp1)); // 对待签名数据做SHA1摘要 SHA1(szEnData, flen, szTmp); //使用私钥对SHA1摘要做签名 ienLen = RSA_sign(NID_sha1, (unsigned char *)szTmp, 20, (unsigned char*)szTmp1, \u0026amp;iRet, prsa); if(ienLen != 1 ) { printf(\u0026#34;签名失败\\n\u0026#34;); RSA_free(prsa); return -1; } RSA_free(prsa); printf(\u0026#34;签名成功\\n\u0026#34;); //签名串szTmp1二进制数据需要转成base64编码 //mac=base64encode(szTmp1)这是伪码,生产MAC值，给对方去校验 //验证签名 //验证签名的是需要获取MAC值，明文签名数据,对“明文签名数据”做SHA1，获得摘要。在对MAC做basedecode(mac)，然后调用函数验证签名 if(NULL == (prsa = getPublicKey(\u0026#34;userpub.key\u0026#34;))) { RSA_free(prsa); printf(\u0026#34;获取私钥失败\\n\u0026#34;); return -1; } flen = strlen(szEnData); printf(\u0026#34;待签名数据:[%s]\\n\u0026#34;, szEnData);//签名数据 和 mac 因该是由通信报文中获得，这里演示直接用使用同一变量 memset(szTmp, 0, sizeof(szTmp)); // memset(szTmp1, 0, sizeof(szTmp1)); // 对待签名数据做SHA1摘要 SHA1(szEnData, flen, szTmp); ienLen = RSA_verify(NID_sha1, (unsigned char *)szTmp, 20, (unsigned char*)szTmp1, iRet, prsa); if(ienLen != 1 ) { printf(\u0026#34;签名不合法\\n\u0026#34;); RSA_free(prsa); return -1; } else printf(\u0026#34;验签成功\\n\u0026#34;); RSA_free(prsa); return 0; } ","date":"2016-12-12T07:44:18-04:00","image":"https://picsum.photos/800/600?r=2024-05-03T07:44:18-04:00","permalink":"https://zfj1441.com/p/c%E8%AF%AD%E8%A8%80%E4%BD%BF%E7%94%A8openssl%E5%AE%9E%E7%8E%B0sha1withrsa%E7%AD%BE%E5%90%8D%E9%AA%8C%E7%AD%BE/","title":"c语言使用openssl实现SHA1WithRSA签名验签"}]