libtv-skills如何手写multipart/form-data上传:upload_file.py源码完整解析

发布时间:2026/10/14 16:09:24
libtv-skills如何手写multipart/form-data上传:upload_file.py源码完整解析 【免费下载链接】libtv-skills项目地址https://gitcode.com/gh_mirrors/li/libtv-skills点击查看免费下载在libtv-skills项目中upload_file.py负责把本地的图片、视频文件上传到 LibTV 的 OSS 存储。它没有引入requests等第三方库而是用 Python 标准库逐字节手写 multipart/form-data 请求体——这正是很多新手觉得上传接口很神秘的地方。本文带你拆解 upload_file.py 的完整源码理解它如何构造边界boundary、拼装表单字段、发起 POST 请求并处理错误。一、为什么选择手写 multipart/form-data很多人上传文件时习惯requestsfiles参数两行代码搞定。但 libtv-skills 有一个核心设计目标仅使用 Python 标准库零依赖安装。这一点在技能描述文件 SKILL.md 中明确写着无需安装额外依赖仅使用 Python 标准库。因此整个scripts/目录包括 create_session.py、query_session.py、download_results.py都基于urllib.request实现 HTTP 请求。upload_file.py也不例外——它把multipart/form-data的魔法拆开了让你看到请求体最原始的样子。核心收益✅ 跨环境即用不需要pip install任何包✅ 请求体结构完全可控、可调试✅ 适合学习 HTTP 协议细节二、源码结构总览先建立全局视角upload_file.py 整体只有约 120 行职责非常清晰行号区间内容作用L14from _common import IM_BASE, ACCESS_KEY复用公共模块的环境变量配置L17ALLOWED_PREFIXES (image/, video/)白名单只允许图片、视频L20-L82upload_file(file_path)核心上传函数L36-L60请求体拼装boundary 两个字段手写 multipart 的关键区段L62-L82发起 POST 并解析响应网络层L85-L116main()命令行入口参数解析 结果输出环境变量配置来自公共模块 _common.pyIM_BASE默认指向https://im.liblib.tvACCESS_KEY从LIBTV_ACCESS_KEY读取未设置时直接报错退出。三、第 1 步上传前的双重校验在动网络之前代码先做两道安检见 upload_file.pyif not os.path.isfile(file_path): print(f错误文件不存在: {file_path}, filesys.stderr) sys.exit(1) # 检查 MIME 类型 mime_type, _ mimetypes.guess_type(file_path) if mime_type and not any(mime_type.startswith(p) for p in ALLOWED_PREFIXES): print(f错误不支持的文件类型: {mime_type}仅支持图片和视频, filesys.stderr) sys.exit(1)文件是否存在os.path.isfile判断路径不存在则立刻退出避免浪费一次网络请求。MIME 类型白名单mimetypes.guess_type根据扩展名猜测类型如a.png→image/png只有image/或video/前缀才放行。 注意这个细节if mime_type的判断意味着当扩展名无法识别MIME 为None时不拦截交给后面的Content-Type: application/octet-stream兜底由服务端最终裁决。这是一种宽容式校验。四、第 2 步逐字节拼装请求体核心这是全文最值得学习的部分。multipart/form-data 的本质是用一串边界boundary把多个表单字段切成块字段之间是头部 空行 二进制内容。4.1 生成唯一的 boundaryboundary f----PythonUpload{uuid.uuid4().hex}用uuid4保证每次请求的边界串都不同避免与服务端解析冲突。这个 boundary 稍后还要写进请求头两处必须严格一致。4.2 拼装两个表单字段请求体共包含两个字段文本字段accessKey和文件字段file对应源码 L39-L58body_parts [] # accessKey 字段普通文本字段 body_parts.append(f--{boundary}\r\n.encode()) body_parts.append(bContent-Disposition: form-data; nameaccessKey\r\n\r\n) body_parts.append(f{ACCESS_KEY}\r\n.encode()) # file 字段文件字段带文件名和内容类型 content_type mime_type or application/octet-stream body_parts.append(f--{boundary}\r\n.encode()) body_parts.append( fContent-Disposition: form-data; namefile; filename{filename}\r\n.encode() ) body_parts.append(fContent-Type: {content_type}\r\n\r\n.encode()) with open(file_path, rb) as f: body_parts.append(f.read()) body_parts.append(b\r\n) # 结束边界 body_parts.append(f--{boundary}--\r\n.encode()) data b.join(body_parts)把body_parts拼接后的完整结构画出来就是这样{...}为占位内容--{boundary}\r\n Content-Disposition: form-data; nameaccessKey\r\n \r\n {ACCESS_KEY}\r\n --{boundary}\r\n Content-Disposition: form-data; namefile; filename{文件名}\r\n Content-Type: image/png\r\n \r\n {文件二进制内容}\r\n --{boundary}--\r\n逐行看懂这 4 个关键点每个字段都以--{boundary}\r\n开头——边界行是该字段的门牌号。文本字段没有 filename也没有 Content-Type头部和值之间只有一个空行\r\n\r\n分隔。文件字段必须带filename和Content-Type:服务端靠这两项识别这是一个文件以及它的类型类型猜不出时兜底为application/octet-stream二进制流。结尾的--{boundary}--注意多了两个短横线表示表单到此结束这是新手最常漏写的地方。4.3 为什么用b.join而不是字符串拼接因为请求体里混着二进制文件数据字符串无法安全承载任意字节。代码全程使用bytes列表body_parts再一次性b.join(body_parts)拼合是处理文件上传的标准姿势。⚠️ 小提示这里会把整个文件读进内存。对 200MB 以内的素材没问题但若是超大文件应改用分块流式上传。五、第 3 步请求头与 POST 发送拼好的请求体交给urllib.request.Request见 upload_file.pyurl f{IM_BASE.rstrip(/)}/openapi/upload req urllib.request.Request( url, datadata, methodPOST, headers{ Authorization: fBearer {ACCESS_KEY}, Content-Type: fmultipart/form-data; boundary{boundary}, }, ) try: with urllib.request.urlopen(req, timeout120) as resp: result json.loads(resp.read().decode(utf-8)) return result.get(data, {})三个细节值得注意细节说明IM_BASE.rstrip(/)去掉末尾斜杠防止拼出https://host//openapi/upload双斜杠Content-Type必须带boundary服务端靠它才知道边界串是什么漏写会解析失败timeout120视频可能较大超时放宽到 120 秒对比 _common.py 中 JSON 接口只有 30 秒鉴权方式与整个项目一致请求头Authorization: Bearer LIBTV_ACCESS_KEY这一点在 README.md 的 API 参考部分有统一说明。六、第 4 步响应解析与错误处理成功时从 JSON 响应的data字段取出 OSS 地址失败时按异常类型分别处理见 upload_file.pyexcept urllib.error.HTTPError as e: err_body e.read().decode(utf-8) if e.fp else print(fAPI 错误 {e.code}: {err_body}, filesys.stderr) sys.exit(1) except urllib.error.URLError as e: print(f网络错误: {e.reason}, filesys.stderr) sys.exit(1)HTTPError服务端返回 4xx/5xx读出响应体原文打印比如类型被拒、文件超限等便于定位。URLError连不上服务器打印网络原因DNS 失败、超时等。所有错误信息统一写到stderr并以sys.exit(1)退出——这样 AI Agent 或非交互环境能可靠地感知失败。七、实际使用一条命令完成上传配置好环境变量后直接执行即可export LIBTV_ACCESS_KEYyour-access-key # 上传图片 python3 skills/libtv-skill/scripts/upload_file.py /path/to/image.png # 上传视频 python3 skills/libtv-skill/scripts/upload_file.py /path/to/video.mp4成功时输出 JSON 格式main()中只保留最精简的url字段{ url: https://libtv-res.liblib.art/claw/{projectUuid}/{uuid}.png }这个 OSS 地址会在创作流程中派上用场先上传参考图/视频再把 URL 拼进消息发给后端例如 SKILL.md 中场景 2用户提供图片/视频要求编辑修改的典型工作流1. upload_file.py /path/to/video.mp4 → 拿到 OSS URL 2. create_session.py 把四周的纸船都换成白色的纸爱心 参考视频{oss_url} 3. 轮询 query_session.py 获取结果八、总结手写 multipart 的 5 个要点学完upload_file.py你掌握了手写 multipart/form-data 上传的完整套路boundary 全局唯一生成一次同时用于请求体和Content-Type请求头。每个字段 边界行 头部 空行 内容头部用Content-Disposition声明字段名。文件字段额外带filename和Content-Type类型未知时用application/octet-stream兜底。结尾必须写--boundary--注意双短横线。全程用bytes拼接二进制数据安全无损。这套零依赖 手写协议的写法让 upload_file.py 在任何有 Python 3.6 的机器上开箱即用也是 libtv-skills 整个技能包保持一致风格的缩影。延伸阅读公共鉴权与 HTTP 封装见 _common.py上传在整体工作流中的位置见 SKILL.md所有脚本用法速查见 README.md。赞分享【免费下载链接】libtv-skills项目地址https://gitcode.com/gh_mirrors/li/libtv-skills点击查看免费下载相关推荐NSwag文件上传处理multipart/form-data的OpenAPI描述NSwag文件上传处理multipart/form data的OpenAPI描述 文件上传是Web开发中的常见需求而 multipart/form data开发工具代码生成API设计5分钟掌握HTTPie CLI文件上传multipart/form-data完全指南5分钟掌握HTTPie CLI文件上传multipart/form data完全指南 HTTPie CLI是一款现代化、用户友好的命令行HTTP客户端特别适CLI开发工具Guzzle文件上传终极指南multipart/form-data完全解析Guzzle文件上传终极指南multipart/form data完全解析 在Web开发中文件上传是常见需求但处理 multipart/form data后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询