当前位置: 首页 > news >正文

视频号下载视频思路

要实现「个微登录企微后转发视频号 → 企微回调获取参数 → 解析视频链接 → 小程序推送」,核心是 适配企微外部联系人(个微登录场景)+ 回调消息解析 + 视频链接拼接 + 小程序消息推送,全程遵循企微官方接口规范,以下是 完整实现方案(含配置 + 代码 + 测试),聚焦「个微登录(外部联系人)」场景适配:

一、核心逻辑与前提说明

1. 完整流程(个微登录场景)

plaintext

个微用户 → 添加企微外部联系人 → 转发视频号到企微聊天框 → 企微触发回调 → 后端解密消息提取 object_id/object_nonce_id → 调用视频号详情接口拼接播放/下载链接 → 后端调用企微发送小程序接口 → 个微用户收到小程序通知 → 点击小程序直接播放/下载视频

2. 关键前提(必须满足)

  • 企微已创建应用(需完成企业认证,个人未认证企业无法使用外部联系人功能);
  • 应用已开启「外部联系人」权限(允许接收个微用户的消息);
  • 企微已关联目标小程序(用于发送小程序消息);
  • 后端服务器支持 HTTPS(企微回调强制要求),公网可访问;
  • 个微用户已添加企微外部联系人(通过企微对外名片添加)。

二、第一步:企微后台核心配置(适配个微登录场景)

1. 应用基础配置(接收外部联系人消息)

登录企微管理后台 → 应用管理 → 选择你的应用(如「视频下载助手」):

(1)配置「接收消息」(回调核心)
  • 开启「接收消息」开关;
  • 选择「加密模式」(推荐,安全且兼容外部联系人消息);
  • 填写配置:
    • 回调 URL:https://你的公网域名/wechat/callback(HTTPS + 公网可达,路径与后端接口一致);
    • Token:自定义字符串(如 wxcp_video_2025),需与后端 application.yml 一致;
    • EncodingAESKey:点击「随机生成」,复制到后端配置(43 位字符,需完全一致);
  • 勾选「接收外部联系人消息」(关键!适配个微登录场景,否则收不到个微用户的转发消息)。
(2)配置「外部联系人权限」
  • 应用详情 → 权限管理 → 找到「外部联系人」相关权限:
    • 勾选「获取外部联系人基本信息」「接收外部联系人消息」「发送外部联系人消息」;
  • 应用可见范围:
    • 内部成员:添加企微内部成员(用于接收个微用户的转发消息);
    • 外部联系人:无需额外设置,个微用户添加企微外部联系人后即可互动。
(3)关联小程序(发送小程序消息必备)
  • 企微管理后台 → 应用管理 → 小程序 → 点击「关联小程序」;
  • 输入你的小程序 AppID → 小程序管理员在微信上确认授权;
  • 关联后,在「可见范围」中勾选「外部联系人」(允许个微用户接收该小程序消息)。

2. 验证回调有效性

  • 配置完成后,点击「回调验证」按钮,提示「验证成功」说明企微能正常访问你的后端;
  • 若验证失败:检查 HTTPS 证书是否有效、URL 是否公网可达、Token/AESKey 是否一致。

三、第二步:服务器 / 网络配置(确保企微能回调)

1. 本地开发(用 ngrok 穿透,无需云服务器)

  • 下载 ngrok:https://ngrok.com/;
  • 执行命令:ngrok http 8080(生成临时 HTTPS 域名,如 https://abc123.ngrok.io);
  • 同步更新企微回调 URL 为 https://abc123.ngrok.io/wechat/callback
  • 保持 ngrok 终端开启(关闭后穿透失效)。

2. 线上部署(云服务器,如阿里云 / 腾讯云)

  • 配置安全组:放行 8080 端口(后端端口)和 443 端口(HTTPS);
  • 部署 HTTPS 证书:用 Let's Encrypt 免费证书或云厂商 SSL 证书,通过 Nginx 反向代理:

    nginx

    server {listen 443 ssl;server_name yourdomain.com; # 你的域名ssl_certificate /usr/local/nginx/conf/cert/yourdomain.pem; # 证书公钥ssl_certificate_key /usr/local/nginx/conf/cert/yourdomain.key; # 证书私钥location /wechat/callback {proxy_pass http://127.0.0.1:8080; # 转发到后端proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;}
    }
    

三、第三步:后端代码实现(核心逻辑)

基于 Spring Boot + 企微 SDK 4.7.8-20251105.104605,适配外部联系人(个微登录)场景,核心实现「回调接收→参数提取→视频解析→小程序推送」。

1. 依赖配置(pom.xml)

xml

<dependencies><!-- Spring Boot核心 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 企微SDK(支持外部联系人+小程序消息) --><dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-cp</artifactId><version>4.7.8-20251105.104605</version></dependency><!-- HTTP客户端 --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.11.0</version></dependency><!-- JSON解析 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.32</version></dependency><!-- 日志 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId></dependency>
</dependencies><!-- 快照仓库(拉取企微SDK快照版) -->
<repositories><repository><id>sonatype-snapshots</id><url>https://oss.sonatype.org/content/repositories/snapshots/</url><snapshots><enabled>true</enabled></snapshots></repository>
</repositories>

2. 配置文件(application.yml)

yaml

wechat:cp:corp-id: 你的企微CorpID # 我的企业→企业信息corp-secret: 你的应用Secret # 应用管理→你的应用→Secretagent-id: 你的应用AgentID # 应用管理→你的应用→AgentID(整数)callback-token: 你的回调Token # 与企微后台一致aes-key: 你的EncodingAESKey # 与企微后台一致(43位)miniapp:appid: 你的小程序AppID # 小程序后台→开发→开发设置page-path: /pages/play/index # 小程序播放/下载页面路径channels:detail-api-url: "https://channels.weixin.qq.com/web/api/feed/detail?eid=export%{objectId}_%{nonceId}"server:port: 8080servlet:context-path: /

3. 企微配置类(初始化 SDK)

java

运行

import com.github.binarywang.weixin.cp.config.WxCpConfigStorage;
import com.github.binarywang.weixin.cp.config.impl.WxCpDefaultConfigImpl;
import com.github.binarywang.weixin.cp.service.WxCpService;
import com.github.binarywang.weixin.cp.service.impl.WxCpServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class WxCpConfig {@Value("${wechat.cp.corp-id}")private String corpId;@Value("${wechat.cp.corp-secret}")private String corpSecret;@Value("${wechat.cp.agent-id}")private Integer agentId;@Value("${wechat.cp.callback-token}")private String callbackToken;@Value("${wechat.cp.aes-key}")private String aesKey;@Beanpublic WxCpConfigStorage wxCpConfigStorage() {WxCpDefaultConfigImpl config = new WxCpDefaultConfigImpl();config.setCorpId(corpId);config.setCorpSecret(corpSecret);config.setAgentId(agentId);config.setToken(callbackToken);config.setAesKey(aesKey);config.setHttpConnectTimeoutMs(10000);config.setHttpReadTimeoutMs(30000);return config;}@Beanpublic WxCpService wxCpService(WxCpConfigStorage configStorage) {WxCpServiceImpl service = new WxCpServiceImpl();service.setWxCpConfigStorage(configStorage);// 启用外部联系人消息支持(适配个微登录场景)service.getExternalContactService();return service;}
}

4. 回调接口(接收企微消息,提取核心参数)

java

运行

import com.github.binarywang.weixin.cp.bean.message.WxCpXmlMessage;
import com.github.binarywang.weixin.cp.bean.message.WxCpXmlOutMessage;
import com.github.binarywang.weixin.cp.service.WxCpService;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/wechat/callback")
public class WxCpCallbackController {private static final Logger log = LoggerFactory.getLogger(WxCpCallbackController.class);@Autowiredprivate WxCpService wxCpService;@Autowiredprivate VideoService videoService;/*** 企微回调验证(GET请求)*/@GetMappingpublic String verifyCallback(@RequestParam("msg_signature") String msgSignature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("echostr") String echostr) {try {String decryptEchostr = wxCpService.decryptEchoStr(msgSignature, timestamp, nonce, echostr);log.info("回调验证成功:{}", decryptEchostr);return decryptEchostr;} catch (Exception e) {log.error("回调验证失败", e);return "验证失败";}}/*** 接收企微消息(POST请求,适配内部/外部联系人)*/@PostMapping(produces = "application/xml;charset=UTF-8")public String receiveMessage(@RequestParam("msg_signature") String msgSignature,@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestBody String requestBody) {try {log.info("接收企微消息:{}", requestBody);// 1. 解密消息WxCpXmlMessage inMessage = WxCpXmlMessage.fromXml(requestBody);inMessage = wxCpService.decryptMessage(inMessage, msgSignature, timestamp, nonce);log.info("解密后消息:{}", inMessage);// 2. 筛选视频号视频消息(msgType=sphfeed,feedType=4)if ("sphfeed".equals(inMessage.getMsgType()) && "4".equals(inMessage.getFeedType())) {String objectId = inMessage.getObjectId();String nonceId = inMessage.getObjectNonceId();String receiverId = inMessage.getToUser(); // 接收人ID(内部成员userId/外部联系人openId)String senderId = inMessage.getFromUser(); // 发送人ID(个微用户=openId,企微用户=userId)// 校验参数if (Strings.isNullOrEmpty(objectId) || Strings.isNullOrEmpty(nonceId)) {log.error("参数缺失:objectId={}, nonceId={}", objectId, nonceId);return buildReply("获取视频信息失败");}// 3. 解析视频链接并发送小程序消息String videoUrl = videoService.parseVideoUrl(objectId, nonceId);if (videoUrl != null) {// 适配外部联系人:发送给个微用户(senderId是外部联系人openId)videoService.sendMiniProgramMessage(senderId, videoUrl);return buildReply("视频解析成功,小程序通知已发送~");} else {return buildReply("视频解析失败,请稍后重试");}}return buildReply("暂不支持该类型消息");} catch (Exception e) {log.error("处理消息异常", e);return buildReply("处理失败");}}/*** 构建企微回复消息*/private String buildReply(String content) {WxCpXmlOutMessage outMessage = WxCpXmlOutMessage.TEXT().content(content).fromUser(wxCpService.getWxCpConfigStorage().getCorpId()).toUser("").build();return outMessage.toXml();}
}

5. 视频解析与小程序推送服务

java

运行

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.weixin.cp.bean.WxCpMessage;
import com.github.binarywang.weixin.cp.service.WxCpService;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import java.net.URLEncoder;
import java.util.concurrent.TimeUnit;@Service
public class VideoService {private static final Logger log = LoggerFactory.getLogger(VideoService.class);private final OkHttpClient okHttpClient;@Autowiredprivate WxCpService wxCpService;@Value("${wechat.channels.detail-api-url}")private String videoDetailApiUrl;@Value("${wechat.miniapp.appid}")private String miniAppId;@Value("${wechat.miniapp.page-path}")private String miniPagePath;public VideoService() {this.okHttpClient = new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).retryOnConnectionFailure(true).build();}/*** 调用视频号详情接口,拼接播放/下载链接(url + urlToken)*/public String parseVideoUrl(String objectId, String nonceId) {try {// 1. 拼接视频号详情接口URLString apiUrl = videoDetailApiUrl.replace("{objectId}", objectId).replace("{nonceId}", nonceId);log.info("调用视频号详情接口:{}", apiUrl);// 2. 模拟浏览器请求头(避免被拦截)Request request = new Request.Builder().url(apiUrl).header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36").header("Referer", "https://channels.weixin.qq.com/").header("Accept", "application/json, text/plain, */*").build();Response response = okHttpClient.newCall(request).execute();if (!response.isSuccessful()) {log.error("接口调用失败,状态码:{}", response.code());return null;}// 3. 解析响应,提取url和urlTokenString responseBody = response.body().string();JSONObject result = JSONObject.parseObject(responseBody);JSONObject data = result.getJSONObject("data");if (data == null) {log.error("响应无data字段:{}", responseBody);return null;}JSONObject objectDesc = data.getJSONObject("object_desc");JSONObject media = objectDesc.getJSONArray("media").getJSONObject(0);String baseUrl = media.getString("url");String urlToken = media.getString("url_token");// 4. 拼接完整播放/下载链接(直接访问可播放/下载)String videoUrl = baseUrl + (baseUrl.contains("?") ? "&token=" : "?token=") + urlToken;log.info("视频链接拼接成功:{}", videoUrl);return videoUrl;} catch (Exception e) {log.error("解析视频链接失败", e);return null;}}/*** 调用企微接口,发送小程序消息(适配内部/外部联系人)*/public void sendMiniProgramMessage(String receiverId, String videoUrl) {try {// URL编码视频链接(避免特殊字符截断)String encodedVideoUrl = URLEncoder.encode(videoUrl, "UTF-8");// 小程序页面路径(携带视频链接参数)String finalPagePath = miniPagePath + "?url=" + encodedVideoUrl;// 构建企微小程序消息WxCpMessage message = WxCpMessage.MINIPROGRAM_NOTICE().agentId(wxCpService.getWxCpConfigStorage().getAgentId()).toUser(receiverId) // 接收人ID:内部成员=userId,外部联系人=openId.title("视频播放通知").appId(miniAppId) // 关联的小程序AppID.page(finalPagePath) // 小程序页面(含视频链接).description("你转发的视频已解析完成,点击直接播放/下载").build();// 发送消息(企微SDK自动处理access_token,支持外部联系人)wxCpService.messageSend(message);log.info("已向{}发送小程序消息,页面路径:{}", receiverId, finalPagePath);} catch (Exception e) {log.error("发送小程序消息失败", e);}}
}

四、第四步:小程序页面开发(播放 / 下载视频)

小程序需创建「播放 / 下载页面」,接收后端传递的视频链接,实现直接播放和下载功能,示例代码如下:

1. 页面结构(pages/play/index.wxml)

xml

<view class="container"><view class="title">视频播放/下载</view><!-- 视频播放组件(直接播放拼接后的链接) --><video src="{{videoUrl}}" class="video-player"controlsenable-play-gesture></video><!-- 下载按钮 --><button bindtap="downloadVideo" class="download-btn">保存视频到手机</button>
</view>

2. 页面样式(pages/play/index.wxss)

css

.container {padding: 20rpx;box-sizing: border-box;
}.title {font-size: 32rpx;font-weight: bold;margin-bottom: 20rpx;text-align: center;
}.video-player {width: 100%;height: 400rpx;background-color: #f5f5f5;border-radius: 16rpx;margin-bottom: 30rpx;
}.download-btn {background-color: #2f54eb;color: white;font-size: 30rpx;border-radius: 8rpx;
}

3. 页面逻辑(pages/play/index.js)

javascript

运行

Page({data: {videoUrl: '' // 接收后端传递的视频链接},onLoad(options) {// 接收并解码视频链接(后端已URL编码)const videoUrl = decodeURIComponent(options.url);this.setData({ videoUrl });console.log("视频链接:", videoUrl);},// 下载视频到手机downloadVideo() {const { videoUrl } = this.data;wx.showLoading({ title: '下载中...' });// 调用微信下载API(需配置小程序download合法域名,或用web-view跳转)wx.downloadFile({url: videoUrl,filePath: `${wx.env.USER_DATA_PATH}/video_${Date.now()}.mp4`,success: (res) => {if (res.statusCode === 200) {// 下载成功后保存到相册wx.saveVideoToPhotosAlbum({filePath: res.filePath,success: () => {wx.hideLoading();wx.showToast({ title: '下载成功!已保存到相册' });},fail: (err) => {wx.hideLoading();wx.showToast({ title: '保存失败,请授权相册权限', icon: 'none' });console.error("保存失败:", err);}});}},fail: (err) => {wx.hideLoading();wx.showToast({ title: '下载失败,请重试', icon: 'none' });console.error("下载失败:", err);}});}
});

4. 小程序配置(app.json)

json

{"pages": ["pages/play/index" // 新增播放页面路径],"window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": "#fff","navigationBarTitleText": "视频助手","navigationBarTextStyle": "black"},"sitemapLocation": "sitemap.json"
}

5. 小程序发布

  • 用微信开发者工具上传代码(版本号如 1.0.0);
  • 小程序后台 → 版本管理 → 提交审核(个人主体 1-2 小时通过);
  • 审核通过后发布「线上版本」(用户可正常访问)。

五、第五步:测试流程(验证完整功能)

  1. 准备工作

    • 个微用户添加企微外部联系人(通过企微对外名片);
    • 启动后端应用,确保 ngrok 穿透正常(本地开发)或云服务器部署成功;
    • 企微后台确认应用回调验证成功。
  2. 测试步骤

    • 个微用户打开视频号,选择任意视频 → 转发 → 选择企微外部联系人;
    • 后端日志打印「接收企微消息」「解密后消息」「视频链接拼接成功」;
    • 个微用户收到企微的「小程序通知」(标题 “视频播放通知”);
    • 点击通知进入小程序,视频自动加载播放,点击「保存视频到手机」可下载到相册。

六、关键适配与问题排查

1. 个微登录(外部联系人)场景适配

  • 确保企微应用已开启「外部联系人」权限(应用管理→权限管理);
  • 发送小程序消息时,receiverId 是外部联系人的 openId(回调消息的 fromUser),企微 API 支持给外部联系人发送小程序消息;
  • 若发送失败:检查应用「可见范围」是否包含外部联系人,或外部联系人是否已授权消息接收。

2. 常见问题排查

问题现象原因解决方案
收不到回调消息回调 URL 不是 HTTPS / 公网不可达用 ngrok 穿透或配置云服务器 HTTPS
解析不到 objectId/nonceId消息类型不是 sphfeed 或 feedType≠4确认转发的是视频号视频(非其他类型)
视频链接无法播放链接拼接错误 / 视频号接口调整打印视频号接口响应,检查 url 和 urlToken 字段
小程序消息发送失败企微未关联小程序企微后台→应用管理→小程序→关联目标小程序
小程序无法下载视频未配置 download 合法域名小程序后台→开发→服务器域名→添加视频链接的域名(或用 web-view 跳转)

总结

核心实现逻辑:企微回调接收外部联系人(个微)的视频号消息 → 提取参数解析视频链接 → 调用企微小程序消息接口推送 → 小程序承载播放 / 下载。关键配置是企微应用的回调、外部联系人权限、小程序关联,代码核心是回调消息解密、视频链接拼接、小程序消息推送,全程适配个微登录场景,确保用户操作流程简洁(转发→收通知→播放 / 下载)。

按以上步骤配置后,即可实现 “个微转发视频号到企微 → 自动推送小程序播放 / 下载链接” 的完整功能,无需用户额外操作,体验流畅。

http://www.dtcms.com/a/580991.html

相关文章:

  • Visual Basic 手工制作工具栏
  • 电话交换机软件和录音转文字服务器部署笔记
  • 常州建站程序衡水高端网站建设
  • Java五大排序算法详解与实现
  • [特殊字符] Vue3 + WebView 双端通信桥:用 TypeScript 构建高可维护的 JSBridge 与 JSSDK
  • 自然科学笔记-微积分
  • iOS 上架要求全解析,App Store 审核标准、开发者准备事项与开心上架(Appuploader)跨平台免 Mac 实战指南
  • iOS app语言切换
  • Search-o1:增强大型推理模型的主动搜索能力
  • 个人笔记|IP分片不用TTL
  • 百汇游戏网站开发商南通网站推广公司
  • 【Linux】权限(2):文件权限的深入理解粘滞位
  • 做网站公司如何选百度广告联盟推广链接
  • BIM+GIS协同:RVT文件转3DTiles的技术路径与场景落地
  • 中颖AFE芯片:SH367303、SH367306 和 SH367309
  • 数据结构—排序算法篇三
  • 从“医疗大模型”向“医疗智能体”架构与路径分析(白皮书草案-上)
  • LeetCode算法日记 - Day 95: 回文子串
  • DockerCompose与多容器编排
  • AngularJS与SQL的集成使用指南
  • 【ZeroRange WebRTC】TWCC 在 WebRTC 中的角色与工作原理(深入指南)
  • 数据结构常见的八大排序算法
  • 个人怎么做网站app推广引流方法
  • 初识光伏逆变器
  • 一文了解LLM应用架构:从Prompt到Multi-Agent
  • MongoDB 内存管理避坑指南:解决高占用、页错误等核心问题,让数据库性能翻倍
  • 关于DNS中毒攻击的解决方案分享
  • 【C++】数据挖掘算法在软件测试中的应用
  • WebSocket 完全指南:从原理到实战,搭建实时通信桥梁
  • STM32项目分享:智能水产养殖系统