最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
PHP 如何通过 LinkedIn API 发布带图片的帖子?
时间:2026-09-13 08:34:01 编辑:袖梨 来源:一聚教程网
PHP 通过 LinkedIn API 发布带图片帖子,需要完成三个独立请求:先用 Images API 初始化上传,取得一次性 `uploadUrl` 和 `urn:li:image`;再把图片二进制上传到该 URL;最后调用 Posts API,把 Image URN 放入 `content.media.id`,同时提交作者、正文、可见性、分发方式和发布状态。图片的公网 URL 不能代替 Image URN,上传成功也不会自动创建帖子。
个人发帖通常需要 `w_member_social`,组织主页发帖需要 `w_organization_social`,且当前成员必须拥有允许的主页角色。所有 REST 请求要携带 Bearer Token、`Linkedin-Version` 和 `X-Restli-Protocol-Version: 2.0.0`。成功创建帖子返回 HTTP 201,帖子 ID 位于 `x-restli-id` 响应头;PHP 客户端必须保留响应头,不能只解析 JSON 正文。
完整状态机
第一步是 initialize,声明图片 owner。第二步是 upload,把原始字节发送到返回的上传地址。
第三步是 create post,引用初始化返回的 Image URN。每一步都有独立状态和错误,不能用一个布尔值概括。
应用数据库至少记录 draft、upload_initialized、binary_uploaded、published 和 failed,支持安全重试与人工排查。
不要继续使用旧分享示例
互联网上很多 PHP 示例仍使用旧的 ugcPosts、assets 注册或 share 接口。LinkedIn 官方当前说明 Posts API 取代 ugcPosts API。
旧字段如 mediaCategory、registerUploadRequest 和 originalUrl 不应与新 Posts API 示例混搭。
实现前固定所用文档版本和 API 版本头,升级时按迁移指南验证载荷。
个人与组织作者
个人作者使用成员 URN,组织作者使用 `urn:li:organization:{id}`。初始化图片和创建帖子的 owner、author 应属于同一发布主体。
不能用个人拥有的图片 URN 创建组织帖子,或反向混用。跨主体资源通常会导致授权或所有权错误。
数据库把账号类型、作者 URN 与令牌身份绑定,不允许调用者任意提交 author 字符串。
权限范围
`w_member_social` 允许代表经过身份验证的成员发布内容。组织发布使用 `w_organization_social`。
组织权限还要结合主页角色,官方列出的可发布角色包括管理员、内容管理员等授权角色。
OAuth Scope 出现在 Token 中不代表对任意组织都有权限,发布前查询并验证目标组织授权。
访问令牌安全
令牌存储在服务端 Secret 管理或加密数据库,不能传到浏览器、日志、异常页面或源码仓库。
每个用户或组织连接保存到期时间与授权主体。到期前刷新或重新授权,不能无限重试 401。
撤销连接时删除本地令牌,并停止所有等待发布的 Job。
版本请求头
`Linkedin-Version` 使用平台要求的 YYYYMM 版本值。不要把教程写作时的月份永久硬编码在散落代码中。
集中配置版本,发布日志记录实际值。升级版本时跑契约测试与沙箱验证。
`X-Restli-Protocol-Version` 使用 2.0.0,并在初始化、查询资源和创建帖子等 API 请求中保持一致。
第一步:初始化图片上传
向 Images API 的 initializeUpload action 发送 POST,JSON 中包含 `initializeUploadRequest.owner`。
成功响应的 `value` 内含 uploadUrl、uploadUrlExpiresAt 和 image。image 就是后续帖子使用的 URN。
立即保存这三个值,并验证 URN 以 `urn:li:image:` 开头。缺字段时不能进入上传步骤。
上传地址会过期
uploadUrl 是一次性或短期授权地址,响应给出过期时间。Job 排队过久时地址可能失效。
上传前检查过期时间并留安全余量;过期后重新初始化,不能继续向旧地址重试。
日志不打印完整上传地址,因为其中可能包含授权查询参数。
第二步:上传二进制
读取本地图片流,把原始字节发送到 uploadUrl。不要把 base64 字符串或 multipart 字段名想当然地套用。
上传请求的具体方法、Content-Type 和头部按初始化与官方文档执行。HTTP 客户端不要自动把流转换成 JSON。
大图使用流式 Body,避免一次把全部内容载入 PHP 内存。
为什么公网图片 URL 不够
Posts API 的图片内容引用 LinkedIn 管理的 Image URN,而不是网页上的 `https` 图片地址。
把 URL 放进 commentary 只会成为正文文字或链接,不会变成原生图片媒体。
如果目标是可点击链接预览,应创建 article 内容,而不是把图片帖子和链接卡片概念混合。
确认图片状态
Images API 不支持同步上传选项,因此上传 HTTP 成功后,资源仍可能需要处理。
稳妥实现可用 Image URN 查询资源状态,等待可用后再创建帖子,并设置最大轮询次数。
不要用固定长时间 sleep;采用短退避,状态失败时保存平台错误并停止发帖。
第三步:创建图片帖子
向 Posts API 发送 POST。author 为成员或组织 URN,commentary 为帖子正文,visibility 通常按业务选择 PUBLIC。
distribution 指定主 Feed 和目标实体,lifecycleState 设置 PUBLISHED。`content.media.id` 使用已上传 Image URN。
媒体还可以带 altText,为无障碍访问提供准确图片说明。
帖子载荷字段
基础字段包括 author、commentary、visibility、distribution、content、lifecycleState 和 isReshareDisabledByAuthor。
不要发送未经文档支持的旧字段。JSON 使用真正布尔值和数组,不能全部序列化成字符串。
服务端先构造数组,再由 JSON 编码器生成载荷,避免手拼 JSON 引号和 Unicode 转义错误。
PHP HTTP 客户端结构
把 LinkedIn 调用封装为专用 Client,提供 initializeImageUpload、uploadImageBytes、getImage 和 createImagePost 方法。
统一添加授权与版本头,但上传到预签名 URL 时不要盲目转发 Bearer Token,严格按上传端要求发送。
每个方法返回类型化结果,包含状态码、响应头、解析正文和请求 ID。
使用 Laravel HTTP Client
Laravel 项目可用 Http facade 发送 JSON 和流式请求。初始化与创建帖子设置 Bearer Token 和 REST 头。
上传二进制可使用 `withBody($stream, $mimeType)`,确保流在重试前能够重新定位或重新打开。
调用 `throw()` 前先按安全规则记录状态码和平台请求 ID,不记录令牌与上传 URL。
原生 cURL 注意事项
若使用 cURL,初始化和创建帖子设置 JSON Content-Type、正确 Content-Length,并保留响应头。
可通过 header callback 收集 `x-restli-id`。不能假设它存在于响应 JSON。
文件上传使用文件句柄和明确大小,完成后关闭资源;网络异常时读取 errno 与 HTTP 状态分别处理。
读取 x-restli-id
Posts API 成功创建返回 201,帖子 ID在 `x-restli-id` 头。部分 HTTP 库的头名称大小写可能不同。
通过客户端的无大小写 Header API 读取,保存完整 URN,不自行截取最后数字作为唯一标识。
201 但缺少 ID 应视为不确定结果,先查询或人工确认,不能立即再次创建导致重复帖子。
幂等设计
为每个发布任务生成内部 idempotency_key,绑定作者、正文哈希、图片哈希和计划时间。
在本地数据库建立唯一约束,只有一个 Worker能进入发布状态。平台调用前后都写事务状态。
网络在提交后断开时,结果可能已经发布。重试前根据已保存资源和平台记录核验。
队列化发布
图片上传与处理可能超过普通 Web 请求时间,控制器只创建发布任务并返回任务 ID。
队列 Job 执行三阶段流程,按账号设置并发和速率限制。同一草稿使用 WithoutOverlapping 或唯一 Job。
状态页面展示处理中、已发布、需重新授权和失败,不让用户反复点击生成重复任务。
失败分类
400 多为载荷、URN 或版本问题,不应原样无限重试。401 需要检查令牌到期,403 检查 Scope 与主页角色。
404 可能是资源、作者或版本失效。429 按平台限流信息退避。5xx 和网络错误可有限重试。
每类错误映射稳定内部代码,界面提供可操作提示,原始响应仅在受限日志保存。
重试边界
初始化失败可以重试;上传 URL 过期则重新初始化。二进制上传失败时要判断地址是否仍有效。
创建帖子超时最危险,因为平台可能已接收。优先查询或标记 `publish_unknown`,不要盲目重发。
使用指数退避和随机抖动,避免大量定时帖子在服务恢复时同时冲击 API。
图片输入验证
上传前验证真实 MIME、尺寸、像素数量和文件大小。扩展名不足以证明文件类型。
解码一次图片,拒绝损坏、超大像素或包含不支持格式的数据。重新编码可清理不必要元数据。
不能让用户提交服务器任意路径或内网 URL,由后端抓取会引入目录遍历或 SSRF。
图片方向与颜色
手机图片可能依赖 EXIF Orientation。上传前按方向旋转并移除定位等敏感 EXIF。
转换色彩空间后生成兼容格式,避免平台预览与本地不同。透明背景需要在目标格式中验证。
保留源图哈希与发布版本,后续审计能确定实际使用哪张图片。
altText
altText 描述图片传递的信息,不重复“图片”字样,也不堆关键词。
装饰图片可以简短,图表应说明关键趋势。文本海报应概括核心文字。
长度与字符限制按当前 API 文档校验,不能让超长替代文本导致整帖失败。
图片帖子与文章帖子
图片帖子使用 `content.media`,点击通常围绕媒体展示,并不等同于外部链接卡片。
需要标题、描述、来源 URL 和缩略图时,使用 `content.article`,缩略图同样可先通过 Images API 获得 URN。
不要指望在图片媒体中增加 originalUrl 就让整张图跳转外站,新旧 API 行为不同。
正文中的 URL
commentary 可包含文字 URL,但呈现方式由 LinkedIn 客户端决定。它不会把图片资产自动转为可点击外链。
若外链是核心内容,选择 article post 并提供 source、title、description 和 thumbnail。
发帖前预览目标类型,避免业务方期待链接卡片却得到普通图片帖。
单图与多图
单图帖子使用 Images API 加 Posts API 的 media。多图自然帖子应使用官方 MultiImage API 流程。
不能把多个 Image URN 随意塞进单图 media 字段。轮播广告又是不同的赞助内容能力。
在应用领域模型中区分 single_image、multi_image、article 和 video,分别验证载荷。
组织主页角色
令牌属于某成员,组织帖子仍依赖该成员在目标 Page 的角色。角色变化后已有连接可能失去发布能力。
发布前或定期同步组织权限,不在 UI 显示无权账号。403 时提示管理员重新检查角色。
不要通过共享个人账号解决组织授权,审计与撤销都会失控。
可见性与分发
visibility 决定内容可见范围,distribution 控制 Feed 分发和目标实体。二者不是同一开关。
普通公开自然帖子使用文档支持的组合。定向、暗帖或赞助内容需要对应产品权限和字段。
服务端只暴露业务允许选项,不把任意 JSON 透传给平台。
内容审核
发布前校验正文长度、空内容、禁止词、提及格式和媒体版权。组织内容可进入人工审批。
审批记录绑定正文哈希、图片哈希、作者 URN 和发布类型。审批后任何变化都重新审核。
定时发布在执行前再次确认账号授权和内容未被撤回。
数据库模型
social_posts 保存内部 ID、author_urn、type、commentary、status、scheduled_at、platform_post_urn 和版本。
social_media 保存文件哈希、本地路径、MIME、image_urn、upload_url_expires_at 与处理状态。
social_publish_attempts 保存阶段、状态码、平台请求 ID、错误类别和时间,不保存秘密。
多租户隔离
账号连接、草稿、图片和任务全部绑定 tenant_id。每次加载发布任务重新验证租户所有权。
author URN 从已授权连接派生,不接收前端任意值。Image URN 也只能引用该草稿已上传资产。
队列 Job只传内部 ID,执行时在服务端重新读取令牌与权限。
Token 加密
数据库令牌使用应用级加密,并由专用密钥保护。日志序列化模型时排除 token 字段。
生产、预发布与开发使用不同 LinkedIn 应用和回调地址,不能共享真实组织令牌。
密钥轮换需要重新加密或支持历史解密键,确保连接不会静默损坏。
速率限制
按应用、成员和组织维度记录调用量。429 后尊重平台返回的限制信息。
初始化、状态查询和发帖都消耗请求,轮询不能过密。队列调度加入全局与账号级漏斗。
达到内部预算时延迟非紧急帖子,并在 UI 显示预计执行时间。
可观测性
记录每阶段耗时、成功率、HTTP 状态、重试次数和上传字节。用 trace_id 贯穿三次请求。
告警区分授权大面积失效、版本头过期、上传失败和 Posts API 故障。
不要把完整 commentary、图片 URL、Bearer Token 或预签名上传地址放入普通指标标签。
契约测试
使用 Mock HTTP Client 验证 initialize 请求的 owner、必要头和 JSON 结构。
验证上传阶段发送原始二进制且不携带错误的 JSON Content-Type。验证创建帖引用同一 Image URN。
模拟 201 并只在响应头提供 x-restli-id,确保客户端能正确保存。
失败测试
覆盖初始化缺 uploadUrl、上传地址过期、图片处理失败、创建帖 401、403、429 和超时未知结果。
验证不可重试错误只执行一次,可重试错误遵守上限,未知结果不会直接重新发帖。
验证令牌、上传 URL 和图片二进制没有出现在日志快照。
集成测试
在受控测试账号发布一张小图,确认平台显示正文、图片和 altText,并能通过返回 URN 查询。
分别测试个人与组织作者,不能用一种成功推断另一种授权也正确。
API 版本升级前重复集成测试,检查字段、响应头和资源状态变化。
清理测试内容
测试帖子应带可识别前缀和运行 ID,完成后按平台能力删除或记录待人工清理。
不能让 CI 在真实公司主页持续发布测试内容。生产发布测试需要独立审批。
测试图片和本地临时文件按保留策略删除,避免个人信息残留。
常见错误
最常见错误是直接把图片 URL 放进帖子、遗漏上传二进制步骤,或把 uploadUrl 当作 Image URN。
其次是 owner 与 author 不一致、缺版本头、使用旧 ugcPosts 载荷、忽略主页角色和不读取响应头。
还有把图片帖当链接卡片、创建超时后盲目重试、在日志泄露预签名 URL 等工程问题。
上线检查清单
确认使用当前 Images API 与 Posts API,版本头集中配置,旧接口字段已移除。
确认个人和组织 Scope、角色、author 与 owner 一致性经过验证。
确认初始化、二进制上传、状态确认和创建帖子分阶段持久化,支持幂等恢复。
确认 201 的 x-restli-id 被读取,未知提交结果不会制造重复帖子。
确认图片验证、altText、Token 加密、日志脱敏、速率限制和契约测试完整。
结论
PHP 发布 LinkedIn 图片帖的关键不是一段 cURL,而是正确实现 Images API 与 Posts API 的三阶段状态机。
先初始化获得 uploadUrl 和 Image URN,再上传原始图片字节,最后把 URN 放进 `content.media.id` 创建帖子。个人与组织使用不同权限,owner 和 author 必须匹配。
生产实现还要保存响应头中的帖子 URN,处理上传过期、资源状态、未知提交、幂等、限流和令牌安全。明确区分原生图片帖与 article 链接卡片,才能得到符合预期且可维护的发布结果。