news 2026/10/5 11:23:32

Python实现邮件与短信通知:SMTP协议与HTTP API全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python实现邮件与短信通知:SMTP协议与HTTP API全解析

1. 先看本质:发送邮件和发短信,其实是两类完全不同的代码

1.1 业务场景:谁的代码里需要“发邮件”和“发短信”

说句实在话,你系统里其他功能做得再花哨,用户可能都感知不到;但一封验证码邮件没到、一条告警短信没收到,用户立刻就会找上门。所以,“发送邮件”和“发送短信”的代码,往往是整个项目里最不显眼、却最不能出差错的部分。

哪些场景需要这类代码?我粗粗列一下,你肯定不陌生:

  • 用户注册、找回密码、二次验证时发验证码
  • 系统监控报警:服务器CPU飙高、磁盘快满、服务宕机时通知运维
  • 电商订单状态变更:下单成功、发货、退款到账
  • 定时任务执行完毕后,把报表或结果推给负责人
  • 用户触达:账单提醒、活动通知、日报周报

这类需求几乎是所有后端系统的“基础设施”。不管你是做电商、做物联网平台、做企业内部系统,还是写爬虫脚本和量化策略,早晚都要碰。特别是这两年物联网设备普及,短信通道成了设备上下线、异常报警的主要通知手段,代码里少不了一个稳定的发送模块。

1.2 技术路径差异:SMTP协议 vs HTTP API

很多新手容易把“发邮件”和“发短信”混为一谈,以为都是一个函数搞定的事。实际上这两者的技术路径完全不同,理解这一点对后面写代码至关重要。

邮件走的是SMTP协议(Simple Mail Transfer Protocol,简单邮件传输协议)。你的代码扮演的是一个“发件客户端”的角色,通过TCP连接到邮件服务商的SMTP服务器,经过认证后,把邮件内容和收件人地址交给服务器,由它负责投递到对方的邮箱系统。这个过程是标准化的,你只要选一家邮件服务商(比如QQ邮箱、网易邮箱、Outlook,或者自己运维的Postfix),拿到服务器地址、端口和账号凭证,代码就能跟它对话。

短信则完全不一样。国内个人用户几乎不可能直接对接运营商的短信网关——因为那是电信级的协议(CMPP、SGIP等),需要企业资质、通道备案,还得跟运营商对接审核,个人根本租不到通道。所以,实际项目中我们走的都是服务商提供的HTTP API:你把手机号、短信内容、签名、模板ID发给服务商,服务商内部的系统再帮你向运营商通道提交下发。对你来说,代码层面就是一次普通的HTTP请求,只是多了一套签名鉴权和内容审核机制。

一句话总结:写发邮件的代码,你是在跟SMTP服务器打交道;写发短信的代码,你是在跟云服务商的HTTP接口打交道。协议、鉴权方式、错误处理逻辑完全不同,别指望一套代码通吃。

2. 把邮件发出去:SMTP、授权码与Python完整代码

2.1 邮件发送的最小链路

先讲邮件。SMTP发送邮件的完整链路并不复杂,你的代码只需要完成三件事:

  1. 建立TCP连接(用SSL或TLS加密)
  2. 用账号密码或授权码完成认证
  3. 调用 sendmail 或类似方法,提交发件人、收件人和邮件内容

选型上我推荐Python的smtplib加email标准库。smtplib负责跟SMTP服务器对话,email.mime系列负责构造邮件内容(正文、附件、HTML)。这两个全是Python自带的,不需要额外安装第三方包,线上环境部署最省心。

服务器地址和端口需要记住:QQ邮箱是smtp.qq.com,网易是smtp.163.com,常见端口有25(明文,很多云厂商默认封禁)、465(SSL加密)、587(STARTTLS)。我强烈建议用465或587,别用25。25端口在云服务器上被运营商和云平台广泛封禁,你本地调试能通、上了服务器就超时,多半就是这个原因。

2.2 完整代码:支持HTML、附件、多个收件人

下面给一个可以直接抄走的函数。我特意把支持项做全了:文本/HTML正文、附件、多收件人、中文文件名,覆盖绝大多数业务需求。

import smtplib import os from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication from email.header import Header from email.utils import formataddr def send_email( smtp_host: str, smtp_port: int, username: str, password: str, # 这里传授权码,不是登录密码 from_name: str, to_list: list, subject: str, body_text: str = "", body_html: str = "", attachments: list = None # 元素为本地文件路径 ): msg = MIMEMultipart("mixed") # 发件人和收件人信息 msg["From"] = formataddr((str(Header(from_name, "utf-8")), username)) msg["To"] = ",".join(to_list) # 中文标题必须用 Header 处理,否则必现乱码 msg["Subject"] = Header(subject, "utf-8") # 纯文本部分 if body_text: msg.attach(MIMEText(body_text, "plain", "utf-8")) # HTML部分(有的客户端优先展示 HTML) if body_html: msg.attach(MIMEText(body_html, "html", "utf-8")) # 附件处理 for file_path in (attachments or []): if not os.path.exists(file_path): continue filename = os.path.basename(file_path) with open(file_path, "rb") as f: part = MIMEApplication(f.read()) # 中文附件名要用 RFC2231 格式,避免乱码 part.add_header( "Content-Disposition", "attachment", filename=("utf-8", "", filename) ) msg.attach(part) # 建立SSL连接并发送 server = smtplib.SMTP_SSL(smtp_host, smtp_port, timeout=10) server.login(username, password) server.sendmail(username, to_list, msg.as_string()) server.quit()

调用方式就是填上你自己的邮箱服务器参数:

send_email( smtp_host="smtp.qq.com", smtp_port=465, username="yourname@qq.com", password="授权码", from_name="监控告警系统", to_list=["admin@example.com"], subject="磁盘使用率超过90%", body_text="请尽快处理。/n服务器 /root 使用率 91%。", )

2.3 为什么是“授权码”而不是密码

我见过太多人第一次写邮件发送代码时,直接拿邮箱登录密码去认证,结果报SMTPAuthenticationError,然后一脸懵。原因很简单:主流邮箱服务商早就禁止第三方客户端使用登录密码登录SMTP了。你需要去邮箱设置里开启SMTP服务,然后生成一个专属的“授权码”。这个授权码只用于邮件客户端和第三方程序登录,泄露了也能单独吊销,不影响邮箱主密码安全。

拿QQ邮箱举例:设置 → 账号 → 开启POP3/SMTP服务,会给你一串16位的授权码。网易类似,在设置里找“客户端授权密码”。

这里有个实践细节:授权码里有空格还是没空格,复制粘贴时极易出错。我习惯在代码里先打印出来做一次登录测试,确认无误后再写进真正的配置,而且授权码不要写死在代码里,放环境变量或配置中心,否则下次别人拿到你仓库源码,你的邮箱就被“裸奔”了。

2.4 中文乱码与编码细节

中文乱码是邮件发送里最常见的坑,根源在于MIME协议默认只支持ASCII字符。解决手段就是我在上面代码里用到的两个关键点:

  • Header(subject, "utf-8"):对邮件主题做Base64编码,这样即使收件人客户端是英文环境,也能正确显示中文主题。
  • formataddr((str(Header(from_name, "utf-8")), username)):发件人昵称也得单独编码,不然“张三”会变成“=?utf-8?B?...?=”一堆乱码。
  • 附件中文名用filename=("utf-8", "", filename)这种RFC2231格式,而不是直接塞原始文件名。很多人踩过这个坑:正文没问题、附件名全是乱码,多半就是这里没处理。

另外提醒一句:发给日韩用户时,正文用纯文本就好,带HTML时最好顺带附一个纯文本版本,有些老旧邮件系统渲染不了HTML,纯文本备胎能兜底。

3. 把短信发出去:服务商API、签名与模板

3.1 为什么个人不能直连运营商网关

先别急着写代码,这个问题不搞清楚,你会一直在错误方向上折腾。

短信发送和邮件的最大区别是:邮件是你自己跟SMTP服务器对接,而短信的最后一公里(从运营商下发到用户手机)是被严管的。个人开发者没有企业资质、没有渠道号、没有内容审核资质,运营商根本不会给你开通道。所以,现实是所有人都要通过短信服务商来发:你在代码里调用服务商的HTTP接口,把你的短信内容、目标号码传过去,服务商内部完成内容审核、号码校验、运营商提交、状态回执这一整套动作。

国内主流服务商包括阿里云、腾讯云、华为云,还有大量中小型短信平台。他们的API风格大同小异:一个HTTP POST请求,带上AccessKeyId、SignName(签名)、TemplateCode(模板ID)、TemplateParam(模板变量)、PhoneNumbers(目标号码),再用一套签名算法做鉴权。

3.2 HTTP API调用链路分解

以阿里云短信服务为例,一次成功的短信发送,在代码层面要经历这四步:

  1. 拼接公共参数和业务参数。公共参数包括AccessKeyId、Action=SendSms、Version=2017-05-25、Format=JSON、SignatureMethod=HMAC-SHA1等;业务参数包括PhoneNumbers、SignName、TemplateCode、TemplateParam。
  2. 生成签名。对请求参数按照字典序排序,拼接成规范化字符串,再用HMAC-SHA1加密,最后Base64编码。服务端会用同样的算法校验签名,防止请求被篡改。
  3. 发起HTTP请求。把参数放在请求体里发送到dysmsapi.aliyuncs.com。
  4. 解析返回结果。返回Code=OK表示受理成功,否则根据错误码排查。

很多人图省事直接用官方SDK,这没问题。但我强烈建议你至少亲手写一次纯HTTP版本,真正理解签名怎么来的。因为你一旦日后要接其他服务商,或者SDK升级导致行为变化,不懂签名只会干瞪眼。

3.3 完整代码:阿里云短信发送(Python)

下面是一个基于官方HTTP接口、不依赖SDK的完整样例。使用前请把ACCESS_KEY_ID、ACCESS_KEY_SECRET替换成你自己账号的密钥,并保证签名、模板都已通过审核。

import json import uuid import time import hmac import hashlib import base64 import urllib.parse import requests ACCESS_KEY_ID = "你的AccessKeyId" ACCESS_KEY_SECRET = "你的AccessKeySecret" # 短信签名和模板ID,需提前在控制台申请并审核通过 SIGN_NAME = "你的签名" TEMPLATE_CODE = "SMS_123456789" def sms_sign(params: dict) -> str: # 1. 参数排序 sorted_keys = sorted(params.keys()) query_string = "" for key in sorted_keys: value = str(params[key]) query_string += f"{key}={urllib.parse.quote(str(value), safe='')}&" query_string = query_string[:-1] # 去掉末尾 & # 2. 构造待签名串 string_to_sign = f"POST&%2F&{urllib.parse.quote(query_string, safe='')}" # 3. HMAC-SHA1 + Base64 signature = base64.b64encode( hmac.new( (ACCESS_KEY_SECRET + "&").encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha1 ).digest() ).decode("utf-8") return signature def send_sms(phone_number: str, template_param: dict): params = { "AccessKeyId": ACCESS_KEY_ID, "Action": "SendSms", "Format": "JSON", "PhoneNumbers": phone_number, "RegionId": "cn-hangzhou", "SignName": SIGN_NAME, "SignatureMethod": "HMAC-SHA1", "SignatureNonce": str(uuid.uuid4()), # 防重放,每次请求必须唯一 "SignatureVersion": "1.0", "TemplateCode": TEMPLATE_CODE, "TemplateParam": json.dumps(template_param, ensure_ascii=False), "Timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), "Version": "2017-05-25" } params["Signature"] = sms_sign(params) resp = requests.post( "https://dysmsapi.aliyuncs.com/", data=params, timeout=10 ) result = resp.json() if result.get("Code") == "OK": return True, result.get("Message") return False, result.get("Message")

调用示例:

ok, msg = send_sms( phone_number="13800138000", template_param={"code": "123456", "product": "监控预警"} ) if ok: print("短信发送受理成功") else: print("失败原因:", msg)

TemplateParam里的键必须和你的短信模板里的变量完全一致。比如你的模板是“您的验证码为${code},有效期${minute}分钟”,那template_param就得写{"code": "123456", "minute": "5"}。多一个、少一个、名字不匹配,服务端都会直接拒绝。

3.4 签名、模板、变量的三条军规

做短信开发,有三条规则是“军规”级别的,违反一条就会在审核或发送环节卡壳。

第一条:签名必须是公司在服务商后台申请审核通过的。签名就是短信开头那一截,比如“【某某科技】”。里面不能含有“测试”“test”这类字眼,也不能是纯个人昵称。审核要人工过,一般几小时到一天。你要是图方便在代码里随意传个签名,返回的错误码大概率是isv.SMS_SIGNATURE_ILLEGAL。

第二条:短信内容必须走模板。国内短信强制要求模板化,不允许直接在接口里拼任意文案。模板也要审核,像“您的订单${goods}已发货”这样,变量部分用${}占位。为什么这么严?主要防止诈骗短信和垃圾广告,宁可审核流程麻烦一点,也别在合规上踩线。

第三条:变量参数要做类型和长度校验。模板变量是字符串,但如果你塞了很长的 JSON,服务端可能拒绝。手机号必须先做正则校验再提交。我见过一个项目把带“+86”前缀的国内号码原样传上去,结果被服务端判定为国际号码,费用翻了三倍还不自知。

4. 实操:搭一个邮件+短信双通道通知模块

4.1 模块设计与配置项

单个函数能发邮件、发短信,不代表项目里就能直接用。真正的工程化做法是把这两个能力封装成一个独立的notifier模块,统一暴露send_by_email()和send_by_sms()两个方法,外部业务只需要调用,不用关心内部细节。

我推荐的目录结构是这样的:

notifier/ ├── __init__.py ├── config.py # 所有配置集中管理 ├── email_sender.py # 邮件发送实现 ├── sms_sender.py # 短信发送实现 ├── errors.py # 自定义异常 └── tests/ └── test_sender.py

配置项统一放config.py,而且从环境变量读取,不写死:

import os EMAIL_CONFIG = { "host": os.getenv("SMTP_HOST", "smtp.qq.com"), "port": int(os.getenv("SMTP_PORT", "465")), "username": os.getenv("SMTP_USER", ""), "password": os.getenv("SMTP_AUTH_CODE", ""), "from_name": os.getenv("MAIL_FROM_NAME", "系统通知"), } SMS_CONFIG = { "access_key_id": os.getenv("SMS_ACCESS_KEY_ID", ""), "access_key_secret": os.getenv("SMS_ACCESS_KEY_SECRET", ""), "sign_name": os.getenv("SMS_SIGN_NAME", ""), "template_code": os.getenv("SMS_TEMPLATE_CODE", ""), }

把敏感信息放环境变量,而不是settings.py里硬编码,是为了防止源码泄露导致密钥被滥用。这个习惯从第一个项目就应该养成。

4.2 核心代码实现

email_sender.py直接复用上一节那段完整的send_email函数,在这里只做一层薄封装。真正有价值的是在设计异常和返回结构上,统一用自定义异常抛出:

class NotifierError(Exception): """通知模块统一异常基类""" pass class EmailSendError(NotifierError): pass

封装后的email_sender.py内部逻辑:取配置 → 调send_email→ 发生SMTPAuthenticationError就转成EmailSendError抛出。这样上层业务用一套try / except NotifierError就能统一兜住所有通知异常,日志也好归类。

sms_sender.py里,我通常会在调用真正的API之前,先做三件事:

  1. 手机号格式校验(国内11位数字,以1开头)
  2. 模板变量字段完整性校验(缺一个字段直接抛异常,避免浪费一次远程调用)
  3. 频率控制(同一个手机号60秒内只允许发一条,用内存里的字典记录时间戳即可)

4.3 测试要点与预期输出

测试环节我踩过几次坑,总结下来有三点最关键:

本地测试时,别用真实手机号狂刷短信。我在开发环境发过几十条测试短信,结果被服务商限流了半天,差点影响线上业务。后来学乖了,本地一律用沙箱号码或邮箱通道代替验证。阿里云等控制台里有测试专用号码,只在联调时用。

邮件测试要检查“这封邮件到底进没进收件箱”。我见过开发环境发邮件成功率100%,但收件人就是收不到——因为测试时用了一个被收件方反垃圾策略误判的域名。联调时,建议用一个专门测试域名,并在真实收件箱里翻开看看有没有进“垃圾邮件”。

把期望输出写进断言。单元测试里,我的做法是对邮件模块做“不真发”测试:用mock替换SMTP连接,断言sendmail是否被正确调用、附件是否正确拼装。短信模块同理,mock掉requests.post,返回固定JSON,断言API参数、签名字段是否齐全。这样CI环境里不会依赖外部网络。

4.4 失败重试、限流与通道降级

通知类操作的可靠性设计,往往比发送本身更值得写。我在生产环境总结出的三个原则:

  • 网络请求必须设超时。SMTP和HTTP请求都给了timeout=10。千万别用默认值无限等,否则一条通知卡半天,连父任务都拖垮。
  • 重试要有退避(backoff)。失败后立刻重试没有意义,我习惯用1s -> 3s -> 9s这种间隔,最多重试三次。短信和邮件通道连续失败三次后,就该告警给负责人,而不是死磕。
  • 双通道降级。我做的监控系统里,短信不通就降级发邮件,邮件不通就降级发短信。两边都失败就写入本地日志文件,等通道恢复后补发。实际项目中这一条救过我好几次——有一次短信通道整体异常,全靠邮件兜底,运维才知道线上服务出了问题。
def send_notification(phone: str, email: str, content: str): # 默认走短信,失败自动降级邮件 try: sms_sender.send(phone, content) return "sms" except NotifierError: # 记录降级日志 logger.warning("短信发送失败,降级到邮件通道") email_sender.send(email, "[降级通知]" + content) return "email"

5. 高频故障与排查实录(含速查表)

5.1 邮件发不出去?按这七个原因逐一排查

邮件发不出去的原因往往不在你的代码里,而在环境、账号、收件方策略之间。我按出现的频率列一下:

  • 错误一:SMTPAuthenticationError。最常见,授权码抄错、授权码过期、邮箱服务未开SMTP。先在本地用单行命令验证:python -c里直接server.login()一次,能过就说明不是账号问题。
  • 错误二:超时。大概率是25端口被云厂商封了。换465或587端口,再看看安全组规则是否放行。
  • 错误三:554/501 发件人被拒。发件人参数与登录账号不一致造成的。很多邮箱服务器规定,登录认证的账号必须和MAIL FROM是同一个,别图省事在代码里硬写一个不存在的发件箱。
  • 错误四:收件人收不到但无报错。去检查 SPF、DKIM、DMARC 记录。你的发送域名如果没有SPF记录,收件方很可能会判为垃圾邮件。内部系统可用企业邮箱服务商解决这个问题。
  • 错误五:附件的文件名全是乱码。用了 RFC2231 方案就没事;直接用filename=filename的必乱。
  • 错误六:内容被拦截。“验证码”“密码”等关键词加上发送频率高,容易被风控。尽量走模板化的内容,别写“hello my friend good morning”这种英文垃圾邮件常见句式。
  • 错误七:smtplib 报SMTPServerDisconnected。连接早期被服务器断开,可能是TLS版本不兼容。升级Python版本到3.7以上一般能解决。

5.2 短信发不出去?这五个坑最隐蔽

短信的问题比邮件更“黑盒”一些,因为服务商返回的错误码经常让人摸不着头脑。实际项目中我绕过的坑:

  • 坑一:SignatureDoesNotMatch。90%是签名串拼接的编码问题,特别是参数值里含中文时,BASE64编码前必须用UTF-8。还有,排序必须按每个参数的key做字典序,不是按拼好的query string排。
  • 坑二:isv.SMS_TEMPLATE_ILLEGAL。模板还没审核通过,或者模板变量名与你传的TemplateParam不一致。去控制台仔细核对模板里的${}变量,确信一个不差。
  • 坑三:isv.BUSINESS_LIMIT_CONTROL。限流了。同一个号码验证码类短信,通常限制为1条/分钟、5条/小时、10条/天。排查时注意看是不是测试时刷太猛。
  • 坑四:阿里云短信api发不出去的隐藏根因。我见过最多的情况是TemplateParam传成了字符串而不是合法的JSON字典。比如直接传"{'code':'123'}",Python的单引号JSON在服务端解析直接失败。请用json.dumps(template_param, ensure_ascii=False)确保输出是双引号合法JSON,中文不要被转义成unicode。
  • 坑五:号码带了国际前缀。国内号码必须不带+86,国际号码要带国家码,否则走国际通道计费,还可能发送失败。手机号校验正则别只写\d{11}。

5.3 问题与解决对照速查表

我把上面的坑整理成一张表,方便你贴在工位旁边或者存进 Wiki。排查的时候照着序号来,基本能解决80%的问题。

现象可能原因处理方式
邮件认证失败授权码错误/未开启SMTP重新生成授权码,逐字符核对
邮件连接超时25端口被封/安全组未放行换465或587端口
邮件发送后进垃圾箱SPF/DKIM缺失配置域名DNS的SPF记录
邮件中文主题乱码未使用Header编码用Header(subject, "utf-8")
附件中文名乱码未使用RFC2231用filename=("utf-8", "", fn)
短信SignatureDoesNotMatch签名串拼接错误重点检查排序、URL编码、UTF-8
短信TEMPLATE_ILLEGAL模板未过审或变量名不匹配控制台核对模板变量
短信BUSINESS_LIMIT_CONTROL触发限流降低发送频率,加本地限流
短信报OK但用户没收到手机号格式/被运营商拦截核对号码、检查模板合规
API调用返回InvalidAccessKeyIdAccessKeyID错误去RAM控制台核对,注意区分中划线

5.4 我从实际项目里总结的避坑经验

最后分享几条压箱底的经验,算不上多高深,但都是我拿真金白银换来的:

经验一:日志要留全链路追踪信息。每次邮件/短信调用,都记录一个唯一 request_id,把收件人、模板ID、服务商返回码、耗时全打出来。出了问题,查日志能省一半时间。别问为什么,你凌晨三点被叫起来排查短信丢失时,就知道这话有多重要。

经验二:敏感信息务必脱敏。日志里不要打完整的手机号,打138****8000就行。虽然内部系统可能信任度高,但日志会同步到日志平台、错误监控系统,甚至可能被人截屏。保护用户手机号是基本修养。

经验三:告警的最终兜底,永远是人。短信通道挂了,就算降级邮件,邮件也可能晚到。所以我在巡检脚本里加了“拨测”逻辑:每天凌晨自动给运维负责人发一条测试短信,收不到就证明通道有问题,提前发现就不用在关键时刻被动。

经验四:把发送逻辑和业务逻辑解耦。别把邮件发送直接写在注册接口里,用户点“注册”三秒才返回,体验极差。正确做法是业务接口只负责落库,把“发送验证码”丢到消息队列或线程池异步执行。发送结果用回调或状态表记录。这样代码结构清爽,性能也不用担心。

这些经验在我参与过的多个项目里反复被验证——通知模块看着简单,细节却格外磨人。刚入门的朋友,建议先把上面的代码跑通、把每个报错都亲手复现一遍,再去做高可用和降级设计。基础扎实了,遇到问题心里就有数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 11:23:31

C# OPC通讯实例:从OPC DA到上位机PLC数据采集的完整实现

简介:面向C#与工控开发者的OPC通讯实例源码包,由工控老马整理并亲测可用,核心解决通过OPC服务器连接PLC进行数据读写的问题。压缩包采用Visual Studio解决方案组织,包含完整可编译的C#工程、可执行程序、界面图片及Word使用说明&a…

作者头像 李华
网站建设 2026/10/5 11:22:16

快手did/edid注册机制全解析:从设备指纹到did_gt流程

做过移动端采集或者App逆向的朋友,应该对设备注册这类逻辑都不陌生。快手这套did体系,在技术社区里讨论度一直很高,但完整把did、did_gt、edid三者关系讲清楚的资料其实很少。我早期研究快手客户端协议时,也在这上面绕了不少弯路—…

作者头像 李华
网站建设 2026/10/5 11:22:06

OpenShell不是软件,而是跨平台Shell抽象协议

1. OpenShell:一个被严重误读的开源项目名称,以及它真实代表的技术图景“OpenShell”这个词最近在技术社区里频繁出现,但几乎每次都被当作某种“万能终端替代品”或“跨平台命令行套件”来讨论。我第一次看到它出现在某次 macOS 重装论坛的置…

作者头像 李华
网站建设 2026/10/5 11:22:06

Vue 3实战:自习室座位预约系统开发与部署全记录

上个月帮学校图书馆做了一套自习室座位预约系统,正赶上期末季“一座难求”的节点上线,每天几千个学生同时进来抢座。前端用 Vue 3 写,从 Vite 初始化到打包放进后端服务里,走完了一整条生产线。这套系统算不上大,但麻雀…

作者头像 李华
网站建设 2026/10/5 11:21:21

Base64 为什么会让数据涨三分之一:原理、长度计算与几个踩过的坑

Base64 大概是所有编码里「用法人人都会、原理少有人讲清」的典型。大多数人第一次接触它是在传图片或者调接口时,照着抄一行代码就完事了;等到某天发现上传的体积莫名超标、或者某段字符串解不开,才开始回头问:它到底在干什么。 …

作者头像 李华
网站建设 2026/10/5 11:20:37

从MusicFree到IAR:插件机制与加载失败排查实战

如果你最近也刷到过musicfree plugins、iar plugins 是干什么的、failed to load plugins web boot: 2 entries did not activate这类热词,却说不清插件到底在玩什么名堂,那这篇东西就是写给你看的。我把“plugins”这个词当成一个横切面来拆&#xff1a…

作者头像 李华